Tuto almost done
This commit is contained in:
parent
f6c345f11b
commit
fc6d7946b0
@ -1,5 +1,73 @@
|
|||||||
|
% Virtualisation légère
|
||||||
|
% Pierre-Olivier Mercier
|
||||||
|
% Samedi 29 novembre 2014
|
||||||
|
|
||||||
# Installation
|
# 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
|
||||||
|
|
||||||
Docker est un outil haut niveau permettant de faire fonctionner
|
Docker est un outil haut niveau permettant de faire fonctionner
|
||||||
@ -214,8 +282,9 @@ RUN apt-get install -y nginx
|
|||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
```
|
```
|
||||||
|
|
||||||
L'instruction `EXPOSE` sera traité plus tard par le client Docker. Il
|
L'instruction `EXPOSE` sera traité plus tard par le client Docker
|
||||||
s'agit de préciser les ports sur lesquels votre image écoute.
|
(é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
|
En utilisant l'option `-P` du `run`, vous allez pouvoir assigner une
|
||||||
redirection de port aléatoire sur la machine hôte vers votre
|
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
|
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
|
À vous de jouer : utilisez l'instruction `COPY` pour afficher votre
|
||||||
propre `index.html` remplaçant celui installé de base par nginx.
|
propre `index.html` remplaçant celui installé de base par nginx.
|
||||||
|
|
||||||
|
|
||||||
## Lancement de commande automatique
|
## 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;"
|
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
|
## 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
|
# 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|...]`.
|
||||||
|
Loading…
Reference in New Issue
Block a user