diff --git a/tutorial/tutorial.md b/tutorial/tutorial.md index 65f1f9e..9ed98cb 100644 --- a/tutorial/tutorial.md +++ b/tutorial/tutorial.md @@ -1,5 +1,73 @@ +% Virtualisation légère +% Pierre-Olivier Mercier +% Samedi 29 novembre 2014 + # Installation +## Noyau Linux + +Ce TP requiert d'avoir un noyau Linux en version 3.8 au minimum. De plus, il +doit être compilé avec les options suivantes : + +``` +General setup ---> + [*] Control Group support ---> + [*] Freezer cgroup subsystem + [*] Device controller for cgroups + [*] Cpuset support + [*] Include legacy /proc//cpuset file + [*] Simple CPU accounting cgroup subsystem + [*] Resource counters + [*] Memory Resource Controller for Control Groups + [*] Memory Resource Controller Swap Extension + [*] Memory Resource Controller Swap Extension enabled by default + [*] Enable perf_event per-cpu per-container group (cgroup) monitoring + [*] Group CPU scheduler ---> + [*] Group scheduling for SCHED_OTHER + [*] Group scheduling for SCHED_RR/FIFO + <*> Block IO controller + -*- Namespaces support + [*] UTS namespace + [*] IPC namespace + [*] User namespace + [*] PID Namespaces + [*] Network namespace +[*] Networking support ---> + Networking options ---> + 802.1d Ethernet Bridging + 802.1Q VLAN Support +Device Drivers ---> + [*] Network device support ---> + MAC-VLAN support + Virtual ethernet pair device + Character devices ---> + -*- Unix98 PTY support + [*] Support multiple instances of devpts +``` + +Une fois que vous aurez installé LXC, vous pouvez vérifier la compatibilité de +la configuration de votre noyau en utilisant la commande `lxc-checkconfig`. + + +## Docker + +### Par le gestionnaire de paquets + +Sous Debian et ses dérivés (Ubuntu, ) + +### Manuellement + +L'équipe en charge de Docker met à disposition un script pour +installer Docker sur n'importe quel système : + +```sh +curl -sSL https://get.docker.com/ | sh +``` + +## LXC + + + # Docker Docker est un outil haut niveau permettant de faire fonctionner @@ -214,8 +282,9 @@ RUN apt-get install -y nginx EXPOSE 80 ``` -L'instruction `EXPOSE` sera traité plus tard par le client Docker. Il -s'agit de préciser les ports sur lesquels votre image écoute. +L'instruction `EXPOSE` sera traité plus tard par le client Docker +(équivalent à l'argument `--expose`). Il s'agit de préciser les ports +sur lesquels votre image écoute. En utilisant l'option `-P` du `run`, vous allez pouvoir assigner une redirection de port aléatoire sur la machine hôte vers votre @@ -227,21 +296,267 @@ docker run -it -P my_webserver /bin/bash service nginx start ``` +Dans un autre terminal, lancer un `docker ps` et consulter la colonne +*PORTS* pour connaître le port choisit par Docker pour effectuer la +redirection. + +Rendez-vous ensuite dans votre navigateur sur http://localhost:49153/. + À vous de jouer : utilisez l'instruction `COPY` pour afficher votre propre `index.html` remplaçant celui installé de base par nginx. ## Lancement de commande automatique -Vous pouvez placer dans un `Dockerfile` une instruction `CMD` +Vous pouvez placer dans un `Dockerfile` une instruction `CMD` qui sera +exécutée si aucune commande n'est passée lors du `run`, par exemple : +``` CMD nginx -g "daemon off;" +``` + +``` +docker build -name=my_nginx . +docker run -d -P my_nginx +``` + +L'option `-d` passée au `run` lance le conteneur en tâche de +fond. Si vous constatez via un `docker ps` que le conteneur s'arrête +directement, retirer cette option pour voir ce qui ne va pas, ou +utilisez la commande `docker logs`. + + +## Volumes + +Il est possible de partager des répertoires entre plusieurs +conteneurs. Pour ce faire, il faut déclarer dans le `Dockerfile` une +ou plusieurs instructions `VOLUME` avec le chemin du répertoire à +considérer comme volume (il est également possible de le faire via +l'option `--volume` du client). Ces deux lignes sont équivalentes : + +``` +VOLUME /var/log/nginx +``` + +``` +docker run -v /var/log/nginx my_nginx +``` + +Pour monter les volumes dans un autre conteneur, on utilise l'argument +`--volume-from` du client, en indiquant le nom du conteneur avec +lequel on souhaite partager les volumes : + +``` +docker run -it --volume-from romantic_archimedes ubuntu /bin/bash +``` + +Vous constaterez que le répertoire `/var/log/nginx` est partagé entre +`romantic_archimedes` et le dernier conteneur lancé. +\newline + +Le concept principal de Docker est de concevoir des conteneurs +applicatifs : on va préférer assigner un unique rôle à un conteneur +(donc géralement on ne va lancer qu'une seule application par +conteneur) et concevoir un service complet en créant un groupe de +conteneur, partageant des données entre-eux par des volumes. + +Une lecture intéressante sur ce sujet est sans doute cet article de +Michael Crosby: http://crosbymichael.com/advanced-docker-volumes.html + +### Data Volume Container + +Dans de nombreuses situation, il est intéressant de séparer les +données de l'application, et donc d'avoir un conteneur exécutant +l'application et un second stockant les données. + +Cela est particulièrement utile dans le cas d'une base de données : on +veut pouvoir mettre à jour le conteneur exécutant le serveur, sans +pour autant perdre les données. + +L'idée derrière le concept de `Data Volume Container` est de partager +un volume avec un conteneur dont le seul rôle est de stocker les +données. + +Il est parfaitement possible de partager un volume avec un conteneur +qui n'est plus lancé. En effet, tant que vous n'avez pas demandé +explicitement à un conteneur d'être supprimé, il est préservé dans un +coin en attendant des jours meilleurs. + +Voici comment on pourrait lancer un conteneur exécutant une base de +données : + +``` +docker run -v /var/lib/mysql --name dbdata busybox +docker run --volume-from dbdata -e MYSQL_ROOT_PASSWORD=mysecretpassword -d mysql +``` + +Le premier conteneur, sans commande passée, va s'arrêter dès son +lancement. Busybox est l'une des plus petites images possédant tous +les outils de base (il est possible d'obtenir un shell en cas de +besoin). Il expose un volume qui sera utiliser comme stockage +persistant. + +Le second conteneur va lancer le serveur MySQL et utiliser le +répertoire partagé pour stocker les données. + +Lorsqu'il y aura besoin de mettre à jour le conteneur MySQL, les +données ne seront pas perdues (et s'il y avait besoin de migrer les +données entre les deux versions des conteneurs, un conteneur +intermédiaire pourrait parfaitement s'en charger). + +Cela facile également les sauvegardes, qui peuvent s'exécuter dans un +conteneur distinct, dédié à la tâche de sauvegarde. + ## Lier les conteneurs -## Data only container +En plus de vouloir partager des répertoires entre deux conteneurs, il +est souvent nécessaire de partager des ports. -## Ambasador +Pour automatiser le partage d'informations sur les IP et ports +exposés, la commande `run` possède l'option `--link` qui permet de +définir dans les variables d'environnement du conteneur que l'on va +lancer. + +Le détail des variables ajoutées dans cette situation est disponible +ici : https://docs.docker.com/userguide/dockerlinks/#environment-variables + +On utiliser généralement cette liaison pour fournir au conteneur +hébergeant un site web dynamique l'IP et le port où trouver la base de +données : + +``` +docker run -e MYSQL_ROOT_PASSWORD=mysecretpassword -d --name db1 mysql +docker run --link db1 my_nginx +``` + +### Ambasador + +Afin d'abstraire le plus possible l'infrastructure sous-jacente et +d'autoriser les migrations de conteneurs, on utilise le modèle +*ambassador*. + +On lancera systématiquement un conteneur entre deux conteneurs que +l'on veut lier : l'ambassadeur. Celui-ci s'occupera de router +correctement le trafic. En cas de changement de route (si l'un des +conteneurs change de machine hôte par exemple), on a simplement à +redémarrer l'ambassadeur plutôt que le conteneur principal. + +La documentation officielle pour ce modèle est disponible à +https://docs.docker.com/articles/ambassador_pattern_linking/ # LXC + +Contrairement à Docker, l'utilisation de LXC est beaucoup plus proche +de l'administration système classique, car l'approche est beaucoup +plus bas niveau. + +## Lancer un conteneur + +Avec le paquet LXC que vous avez installé, vous avez également +récupéré un certain nombre de modèles de système (souvent installés +dans le dossier `/usr/share/lxc/templates/`). + +La méthode la plus simple pour lancer un conteneur LXC est d'utiliser +l'un de ces modèles qui va installer tout un environnement pour +vous. On utilise pour cela la commande `lxc-create` : + +``` +lxc-create --name toto_first --template ubuntu +``` + +Ce modèle va créer un dossier dans `/var/lib/lxc/` portant le nom que +vous avez précisé. Ce dossier va contenir la configuration LXC du +conteneur, la table des partitions s'il y a besoin de faire des +montages particuliers et enfin le dossier `rootfs` contenant le +système en lui-même. + +On peut maintenant démarrer le conteneur : + +``` +lxc-start --name toto_first +``` + +À la différence de Docker qui va ne lancer que l'application (ou les +applications listées dans la ligne de commande) dans son +environnement, LXC va appeler `/sbin/init` et démarrer tous les +services que l'on peut s'attendre à trouver dans n'importe quelle +machine virtuelle plus classique (la seule différence réside donc dans +le fait que le noyau est partagé avec l'hôte). + +Généralement on lance `lxc-start` avec l'option `--daemon`, puis on +utilise `lxc-console` qui permet de se détacher de la console via le +binding `^A+q`. + +Connectez-vous, lancez quelques commandes puis éteignez la machine +avec `sudo poweroff` ou dans un autre terminal via `lxc-stop --name +toto_first`. + + +## Persistance des données + +Contrairement à Docker, lorsque vous arrêtez un conteneur, les +modifications apportées sont conservées. Si vous appelez à nouveau +`lxc-start --name toto_first`, vous constaterez que votre historique +contient les dernières commandes que vous avez tapé et si vous avez +apporté d'autres modifications sur le système, celles-ci sont toujours +visibles. + + +## Le réseau + +Le modèle ubuntu que vous avez utilisé initialise un fichier de +configuration sans paramètres pour le réseau. Vous n'avez donc pas +d'interface dans le conteneur pour le connecter au réseau. + +Un excellent article détaillant les différents types de réseau est +accessible à : +https://blog.flameeyes.eu/2010/09/linux-containers-and-networking + +N'ayant qu'une seule interface physique sur la machine et n'ayant pas +accès à la configuration des VLAN de la pièce, il ne nous reste que +deux méthodes pour obtenir du réseau dans nos conteneurs : Virtual +Ethernet ou MACVLAN. + +### Virtual Ethernet + +Virtual Ethernet est la configuration la plus simple. On met en place un pont +sur la machine hôte, puis on crée une interface `veth` par conteneur +que l'on veut lancer. On n'oubliera pas d'ajouter ces interfaces au pont. + +Voici un extrait de configuration correspondant au paramétrage d'une +interface `eth0` pour un conteneur donné : + +``` +lxc.network.type = veth +lxc.network.flags = up +lxc.network.link = br0 +``` + +Cette technique a pour inconvénient de laisser au noyau le soin de +router les paquets selon leur adresse IP, ce qui peut être lent et +coûteux étant donné que la carte est placé en mode de promiscuité. + + +### MACVLAN + +Ici, le noyau va orienter les paquets en fonction de leur adresse MAC +de destination. + +``` +lxc.network.type = macvlan +lxc.network.macvlan.mode = bridge +lxc.network.flags = up +lxc.network.link = br0 +``` + + +## Stockage + +Par défaut, le stockage se fait dans l'arborescence du système hôte, +mais il est possible d'utiliser d'autres backends tels que Btrfs, LVM, +overlayfs, AUFS ou ZFS. + +Pour utiliser un type de stockage particulier, préciser lors de la +création de l'environnement du conteneur `-B [btrfs|zfs|lvm|...]`.