Tuto almost done

This commit is contained in:
nemunaire 2014-11-28 17:59:04 +01:00
parent f6c345f11b
commit fc6d7946b0

View File

@ -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/<pid>/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 --->
<M> 802.1d Ethernet Bridging
<M> 802.1Q VLAN Support
Device Drivers --->
[*] Network device support --->
<M> MAC-VLAN support
<M> 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|...]`.