Tag Archives: docker

Dockerisation du service cobbler

Étude d’un système cobbler existant

Un système cobbler comporte plusieurs services:

  • le service cobbler à proprement parlé
  • un service web (apache) pour distribuer les RPMs
  • un service de transfert de fichier basique (tftp) pour distribuer le noyau bootable permettant d’afficher le menu, et tout ce qui va avec.

L’analyse du système existant nous montre

  1. Nous n’avons pas besoin de gérer le DHCP et le DNS, ceux-ci sont déjà gérés sur notre réseau et seul l’adresse du next_server sera mis à jour dans la configuration du DHCP.
  2. On utilise plusieurs snippets additionnels pour configurer la cible dans la phase post-installation (notamment une mise à jour de la distribution, l’installation du dépôt epel, etc…).
  3. Seulement quelques répertoires contiennent des données personnalisées:
    • /var/lib/cobbler: Ce répertoire contient deux types de données:
      • Les Données dynamiques propres à chaque installation et qui doivent être persistantes. Elles seront donc intégrées dans un ou plusieurs volumes de données: /var/lib/cobbler/config (la configuration dynamique) et /var/lib/cobbler/backup (le répertoire des sauvegardes)
      • Les Données semi-statiques. Elles seront intégrées directement dans l’image docker: les modèles de kickstart (définition de l’installation), les snippets (bout de code ré-utilisable), etc…
    • /var/www/cobbler: qui contient le dépôt de paquets RPM des distributions importées. Ces fichiers sont servis par le serveur web et ce répertoire sera intégré dans un volume de données.
    • /var/lib/tftp: qui contient le noyau bootable (pxelinux.0) , le menu d’installation, etc… Ces fichiers sont servis par le serveur tftpd et ce répertoire sera intégré dans un volume de données
    • On aura besoin d’un cinquième point de montage (/mnt sur le conteneur) où les isos des distributions seront montées par le serveur hôte.
      mkdir /mnt/centos
      mount -o ro /path/to/iso/CentOS-7-x86_64-DVD-1804.iso /mnt/centos
    • Et même d’un sixième point de montage, si l’hôte du conteneur a un noyau inférieur à 4.7, à cause du bug concernant les sockets unix sur overlayfs (lien): /var/run/supervisor

Bien entendu, pour ajouter un modèle de kickstart ou un snippet, l’image docker devra être reconstruite. Mais à l’usage, on se rend compte qu’on ajoute/modifie moins de fichiers snippets ou kickstart, que la disponibilité des mises à jour du paquet cobbler.

Démarrage

L’image peut être trouvée sur docker.io

docker pull tartarefr/docker-cobbler

Les variables d’environnement

Le service docker peut être personnalisé via les variables d’environnement suivantes:

  • HOST_IP_ADDR: Cette variable est la seule variable obligatoire car c’est elle qui permet la connexion à l’API cobbler et ne peut pas avoir de valeur par défaut. Elle doit prendre la valeur de l’adresse IP de l’hôte hostname --ip-address | awk '{print $1}'
  • HOST_HTTP_PORT: Port de l’hôte branché sur le port 80 du conteneur. Par défaut c’est 80 (-p 80:80)
  • DEFAULT_ROOT_PASSWD: Mot de passe en clair du compte superutilisateur root qui sera configuré sur les cibles (défaut: cobbler)
  • COBBLER_WEB_USER: Login de cobbler web (défaut: cobbler)
  • COBBLER_WEB_PASSWD: Mot de passe de cobbler web (défaut: cobbler)
  • COBBLER_WEB_REALM: Royaume de cobbler web car c’est de l’authentification digest (défaut: cobbler)
  • COBBLER_LANG: La langue à configurer lors de l’installation des cibles (défaut: fr_FR)
  • COBBLER_KEYBOARD: Le clavier à configurer lors de l’installation des cibles (défaut: fr-latin9)
  • COBBLER_TZ: Le fuseau horaire à configurer lors de l’installation des cibles (défaut: Europe/Paris)

Mise en place avant le premier démarrage

  1. On commence par télécharger l’iso DVD de centos 7
  2. Il va nous falloir construire 5 volumes docker pour un conteneur (même ratio que le pastis)
    docker volume create cobbler_www
    docker volume create cobbler_tftp
    docker volume create cobbler_config
    docker volume create cobbler_backup
    docker volume create cobbler_run
    
  3. On créé le point de montage de notre iso
    sudo mkdir /mnt/centos
  4. On monte notre iso dans /mnt/centos
    sudo mount -o ro /path/to/iso/CentOS-7-x86_64-DVD-1804.iso /mnt/centos
    

Démarrage

Je conseille fortement de modifier la valeur de DEFAULT_ROOT_PASSWD. En effet c’est un des tout premiers mot de passe root essayé par les pirates.

Contrairement à la valeur par défaut, notre serveur web (api et cobbler_web) sera accessible depuis l’hôte sur le port 60080.

docker run -d --privileged \
           -v cobbler_www:/var/www/cobbler:z \
           -v cobbler_tftp:/var/lib/tftp:z \
           -v cobbler_config:/var/lib/cobbler/config:z \
           -v cobbler_backup:/var/lib/cobbler/backup:z \
           -v cobbler_run:/var/run/supervisor:z \
           -v /mnt/centos:/mnt:z \
           -e DEFAULT_ROOT_PASSWD=cobbler \
           -e HOST_IP_ADDR=$(hostname --ip-address | awk '{print $1}') \
           -e HOST_HTTP_PORT=60080 \
           -e COBBLER_WEB_USER=cobbler \
           -e COBBLER_WEB_PASSWD=cobbler \
           -e COBBLER_WEB_REALM=Cobbler \
           -e COBBLER_LANG=fr_FR \
           -e COBBLER_KEYBOARD=fr-latin9 \
           -e COBBLER_TZ=Europe/Paris \
           -p 69:69/udp \
           -p 60080:80 \
           -p 60443:443 \
           -p 25151:25151 \
           --name cobbler \
           tartarefr/docker-cobbler:latest

Initialisation

Une fois à l’intérieur du conteneur, on va ajouter la cible memtest à notre menu d’installation, puis on va importer notre distribution centos 7, ajouter un profile supplémentaire qui permettra d’installer centos 7 avec un environnement graphique et synchroniser le tout.

  1. Immersion dans notre conteneur
    docker exec -ti cobbler /bin/bash
  2. Ajout de la cible memtest. On vérifie au préalable que le fichier /boot/memtest86+-5.01 existe bien (la version peut être modifiée et la ligne suivante doit être adaptée en conséquence)
    cobbler image add --name=memtest86+ --file=/boot/memtest86+-5.01 --image-type=direct
  3. Import de la distribution centos 7
    cobbler import --path=/mnt --name=CentOS-7-x86_64
  4. Ajout du profile pour une installation d’une centos desktop
    cobbler profile add --name=CentOS-7-x86_64-Desktop \
        --distro=CentOS-7-x86_64 \
        --kickstart=/var/lib/cobbler/kickstarts/sample_end.ks \
        --virt-file-size=12 \
        --virt-ram=2048
    
    cobbler profile edit --name CentOS-7-x86_64-Desktop --ksmeta="type=desktop"
    
  5. Synchronisation
    cobbler sync


Malheureusement, le changement de fichier iso (démontage et le montage d’un autre iso sur le même point de montage) sur l’hôte ne met pas à jour le contenu du répertoire /mnt dans le conteneur.
Pour ajouter une autre distribution, il faudra:

  1. stopper le conteneur
  2. détruire le conteneur
  3. démonter l’iso sur l’hôte
  4. monter le nouvel iso
  5. démarrer un nouveau conteneur

Modification de l’image

Pour ajouter un modèle de kickstart ou un snippet, il suffit de placer le fichier dans le répertoire idoine et de modifier le fichier Dockerfile:

  1. Ajouter une instruction docker de copie du fichier dans l’image du conteneur
  2. Si c’est un snippet s’éxecutant dans la phase post-installation, ajouter le nom à la liste des snippets post-installation à l’instruction d’activation (Activate personnal snippets)
  3. Reconstruire l’image
  4. Déployer la nouvelle image

Quelques mots sur l’image docker

J’ai préféré construire une image en partant de zéro, car celles disponibles sont soit plus mise à jour, soit elles utilisent systemd. Je préfère garder cette option pour les cas particuliers, inutile de sortir le tank pour écraser un moustique.

Supervisord est un bien meilleur candidat pour le poste d’orchestrateur dans un conteneur (le bon outil pour la bonne tâche) et permet de redémarrer un ou plusieurs services.

supervisorctl restart cobblerd

L’ensemble du projet cobbler est versionné sur gitlab, et en copie sur github (pour la construction automatique des images sur docker.io).

Fichier Dockerfile
FROM centos:7

MAINTAINER Didier FABERT (tartare) <didier@tartarefr.eu>

# RPM REPOs
RUN yum install -y \
    epel-release \
    && yum clean all \
    && rm -rf /var/cache/yum

RUN yum update -y \
    && yum clean all \
    && rm -rf /var/cache/yum

RUN yum install -y \
  cobbler \
  cobbler-web \
  pykickstart \
  debmirror \
  curl wget \
  rsync \
  supervisor \
  net-tools \
  memtest86+ \
  && yum clean all \
  &&  rm -rf /var/cache/yum

# Copy supervisor conf
COPY supervisord/supervisord.conf /etc/supervisord.conf
COPY supervisord/cobblerd.ini /etc/supervisord.d/cobblerd.ini
COPY supervisord/tftpd.ini /etc/supervisord.d/tftpd.ini
COPY supervisord/httpd.ini /etc/supervisord.d/httpd.ini

# Copy personnal snippets
COPY snippets/partition_config /var/lib/cobbler/snippets/partition_config
COPY snippets/configure_X /var/lib/cobbler/snippets/configure_X
COPY snippets/add_repos /var/lib/cobbler/snippets/add_repos
COPY snippets/disable_prelink /var/lib/cobbler/snippets/disable_prelink
COPY snippets/systemd_persistant_journal /var/lib/cobbler/snippets/systemd_persistant_journal
COPY snippets/rkhunter /var/lib/cobbler/snippets/rkhunter
COPY snippets/enable_X /var/lib/cobbler/snippets/enable_X
COPY snippets/yum_update /var/lib/cobbler/snippets/yum_update

# Copy personnal kickstart

# Activate personnal snippets
RUN for kickstart in sample sample_end legacy ; \
    do \
        additional_post_snippets="" ; \
        for snippet in \
                        add_repos \
                        disable_prelink \
                        systemd_persistant_journal \
                        rkhunter \
                        enable_X \
                        yum_update ; \
        do \
          additional_post_snippets="${additional_post_snippets}\n\$SNIPPET('${snippet}')" ; \
        done ; \
        sed -i \
           -e "/post_anamon/ s/$/${additional_post_snippets}/" \
           -e "/^autopart/ s/^.*$/\$SNIPPET('partition_config')/" \
           -e "/^skipx/ s/^.*$/\$SNIPPET('configure_X')/" \
       /var/lib/cobbler/kickstarts/${kickstart}.ks ; \
    done

# Install vim-enhanced by default and desktop packages if profile have el_type set to desktop (ksmeta)
RUN echo -e "@core\n\nvim-enhanced\n#set \$el_type = \$getVar('type', 'minimal')\n#if \$el_type == 'desktop'\n@base\n@network-tools\n@x11\n@graphical-admin-tools\n#set \$el_version = \$getVar('os_version', None)\n#if \$el_version == 'rhel6'\n@desktop-platform\n@basic-desktop\n#else if \$el_version == 'rhel7'\n@gnome-desktop\n#end if\n#end if\nkernel" >> /var/lib/cobbler/snippets/func_install_if_enabled

COPY first-sync.sh /usr/local/bin/first-sync.sh
COPY entrypoint.sh /entrypoint.sh
RUN chmod 755 /entrypoint.sh /usr/local/bin/first-sync.sh

EXPOSE 69 80 443 25151

VOLUME [ "/var/www/cobbler", "/var/lib/tftp", "/var/lib/cobbler/config", "/var/lib/cobbler/backup", "/var/run/supervisor", "/mnt" ]

ENTRYPOINT /entrypoint.sh

Lancement de la construction

docker build -t local/cobbler .

En cas de problème, ne pas hésiter à regarder le fichier /var/log/cobbler/cobbler.log

Découverte de docker swarm

Pour découvrir le swarm (la nuée ou l’essaim) de docker, on va partir de notre poste Fedora, avec une installation de libvirt fonctionnelle.

Installation

On va créer deux nouvelles machines virtuelles que l’on nommera worker1 et worker2: 2 vCPUs et 4Go de RAM.

Sur l’hôte servant de manager, on initialise le swarm (aka le poste Fedora), en précisant l’adresse IP sur la patte libvirt

docker swarm init --advertise-addr 192.168.122.1
Swarm initialized: current node (2kdz664ro2rpjh5pn1743gsez) is now a manager.
    
To add a worker to this swarm, run the following command:

docker swarm join \
       --token SWMTKN-1-5od9m6ccyazf6uqnopqv6f1p2xqu4saiul7yvbqvjkw5ryczyz-8mjxk1qv26isjnzzyzpf02w5e \
       192.168.122.1:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

Sur les deux hôtes servant de workers: worker1 et worker2

  1. On installe la version la plus récente de docker
    yum install docker docker-latest
  2. On lance le daemon docker
    systemctl start docker
  3. On se joint au swarm en précisant le token fourni à l’initialisation du swarm sur le manager.
    docker swarm join --token SWMTKN-1-54yhh9qjmwcsxb4jhp7p6a0isc4in0vykqu3ir4y06aq6xtpbb-8mspc4bbxh32nx8obni897fvn 192.168.122.1:2377
    This node joined a swarm as a worker.

Le swarm est en place, il ne reste plus qu’à l’utiliser.

Visualiseur

Pour faciliter la vision des choses, on va mettre en place un visualiseur, qui sera encore un conteneur docker.

docker run -it -d \
-p 5000:8080 \
-v /var/run/docker.sock:/var/run/docker.sock \
--name swarm_visualizer \
dockersamples/visualizer

On accède maintenant au visualizer en allant sur l’url http://192.168.122.1:5000/

Gestion des services

Docker swarm gère des services qui seront associés en interne à un ou plusieurs conteneurs. Dans cette exemple (phpmyadmin), on va le découper en deux services:

  • la base de données
  • le serveur web avec le module php

On aurait pu découper en trois, en n’installant pas le module php avec le serveur web et utiliser un service php-fpm, mais cela aurait complexifié inutilement l’exemple.

Afin que nos conteneurs puisse se parler entre eux, on va créer un réseau dédié (overlay)

docker network create --driver overlay --subnet 172.255.0.0/24 swarmnet

On démarre le conteneur embarquant la base de données. Quelques remarques sur la commande à lancer:

  • On fixe la contrainte d’exécution sur le manager, car le conteneur a un volume attaché et docker n’a pas de solution de synchonisation des volumes en natif. En effet si le conteneur s’exécutait sur une autre hôte (worker), il aurait un nouveau volume vierge et on oublie la persistance des données. Toutefois, on pourrait fixer la contrainte d’exécution sur un worker particulier avec --constraint node.hostname=worker1 afin de toujours se servir du même volume de données.
  • On monte le volume docker s’appelant pma-db-data en lieu et place du répertoire de travail mariadb.
  • On fixe le nom de la base de données, les identifiants utilisateur et le mot de passe du superutilisateur (root) via des variables d’environnement.
  • On précise que le conteneur est accessible via son nom d’hôte (accès par rotation DNS) et comme on n’a qu’un seul conteneur base de données, on est sûr de tomber toujours sur le même). Sans ce paramètre (accès par vip) il aurait fallut brancher le port 3306 sur l’hôte exécutant docker (le poste Fedora) et de connecter le conteneur web à l’adresse IP de l’hôte.
  • Évidemment, on le branche sur notre réseau dédié
docker service create --name pma-db \
--constraint node.role==manager \
--mount type=volume,src=pma-db-data,dst=/var/lib/mysql \
-e DB_NAME=pmadb \
-e DB_USER=pma \
-e DB_PASS=pmapasswd \
-e DB_ROOT_PASS=rootpasswd \
--endpoint-mode dnsrr \
--network swarmnet \
docker.io/didier13150/mariadb

On démarre maintenant le second conteneur qui contient le serveur web (avec le module php) et phpmyadmin sur un des workers

  • On fixe la contrainte d’exécution sur un worker
  • On lui communique le nom d’hôte, le nom de la base de données, ainsi que les identifiants utilisateur via des variables d’environnement.
  • On connecte le port 80 du conteneur sur le port 8080 du manager, même si le(s) conteneur(s) s’exécute(nt) sur le(s) worker(s)
  • On le branche également sur notre réseau dédié (pour la résolution du nom d’hôte pma-db)
docker service create --name pma-web \
--constraint node.role==worker \
-e DB_NAME=pmadb \
-e DB_USER=pma \
-e DB_PASS=pmapasswd \
-e DB_HOST=pma-db \
-p 8080:80 \
--network swarmnet \
docker.io/didier13150/phpmyadmin

On accède maintenant à phpmyadmin en allant sur l’url http://192.168.122.1:8080/

Rien de bien compliqué jusqu’à présent.

Mais imaginons qu’un seul serveur web ne puisse tenir la charge et qu’il faudrait le cloner (même plusieurs fois) pour absorber la charge, disons 4 instances de ce serveur web serait pas mal. La magie de swarm, c’est que c’est faisable à chaud, quasiment instantanément et en une seule petite commande:

docker service scale pma-web=4

Et encore plus fort, imaginons que le pic de charge est passé et que les clones ne servent plus à rien, et bien on peut les supprimer:

docker service scale pma-web=1

Pour arrêter les services, on les supprime

docker service rm pma-web
docker service rm pma-db

On arrête aussi notre réseau

docker network rm swarmnet

Gestion des nœuds

Étudions maintenant la gestion des nœuds au sein de notre essaim

  • Lister les nœuds du swarm
    docker node ls
    ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
    2kdz664ro2rpjh5pn1743gsez * manager1 Ready Active Leader
    0xj7x2kxmjuvyqj19ekdubs99 worker1 Ready Active
    7xqkaxxbzi4260b6drujhis1q worker2 Ready Active
  • Supprimer un nœud
    docker node rm 7xqkaxxbzi4260b6drujhis1q