Ready for j1
This commit is contained in:
parent
150f5a8834
commit
dc8544c0c9
31 changed files with 690 additions and 546 deletions
|
|
@ -1,13 +1,16 @@
|
|||
SOURCES = tutorial.md installation.md lxc.md cgroups.md namespaces.md
|
||||
TEMPLATE = ../../template.tex
|
||||
SOURCES = tutorial.md installation.md what.md first.md dockerfile.md volumes.md linking.md cleaning.md rendu.md
|
||||
PANDOCOPTS = --latex-engine=xelatex \
|
||||
--standalone \
|
||||
--normalize \
|
||||
--number-sections \
|
||||
--smart \
|
||||
-M lang=frenchb \
|
||||
-M fontsize=12pt \
|
||||
-M papersize=a4paper \
|
||||
--template=${TEMPLATE}
|
||||
-M mainfont="Linux Libertine O" \
|
||||
-M monofont="Inconsolata" \
|
||||
-M sansfont="Linux Biolinum O" \
|
||||
--include-in-header=../header.tex
|
||||
|
||||
|
||||
all: tutorial.pdf
|
||||
|
|
|
|||
|
|
@ -1,195 +0,0 @@
|
|||
\newpage
|
||||
|
||||
# Utiliser les *cgroup*s
|
||||
|
||||
Les *cgroup*s (pour *Control Group*s) permettent de collecter des statistiques
|
||||
sur des groupes de processus (appelés tâches) et de leur attribuer des
|
||||
propriétés, comme par exemple pour leur imposer des limitations d'utilisation
|
||||
de ressources ou altérer leurs priorités.
|
||||
|
||||
|
||||
## Premiers tests
|
||||
|
||||
Nous allons commencer par faire quelques tests avec le *cgroup* freezer, qui
|
||||
permet d'interrompre l'exécution d'un groupe de processus et de la reprendre.
|
||||
|
||||
### Montage du *cgroup*
|
||||
|
||||
En fonction de la configuration de votre système, il est possible que les
|
||||
*cgroup*s ne soient pas montés au démarrage dans `/sys/fs/cgroup/`. Si vous n'avez
|
||||
pas de dossier `freezer` ou si celui-ci est vide, monter-le en suivant la
|
||||
procédure suivante :
|
||||
|
||||
```
|
||||
mkdir /sys/fs/cgroup/freezer/
|
||||
mount -t cgroup -o freezer none /sys/fs/cgroup/freezer/
|
||||
```
|
||||
|
||||
Cette dernière commande monte le groupe de processus racine, pour le *cgroup*
|
||||
freezer. Tous les dossiers contenu dans cette racine sont des sous-groupes de
|
||||
cette dernière.
|
||||
|
||||
### Création d'un nouveau groupe
|
||||
|
||||
La première étape dans l'utilisation d'un *cgroup* est de créer un nouveau
|
||||
groupe.
|
||||
|
||||
Pour créer un groupe, il suffit de créer un nouveau dossier dans un groupe
|
||||
existant, par exemple la racine :
|
||||
|
||||
```
|
||||
mkdir /sys/fs/cgroup/freezer/virli/
|
||||
ls /sys/fs/cgroup/freezer/virli/
|
||||
```
|
||||
|
||||
Vous avez maintenant un nouveau groupe de processus `virli` dans le *cgroup*
|
||||
Freezer. Comme il s'agit d'une hiérarchie, le groupe `virli` hérite des
|
||||
propriétés de son (ses) père(s).
|
||||
|
||||
### Rattachement de processus
|
||||
|
||||
Pour le moment, ce nouveau groupe ne contient aucune tâche.
|
||||
|
||||
Ouvrons un nouveau terminal (c'est lui que l'on va freezer), et récupérons son
|
||||
PID : `echo $$`.
|
||||
|
||||
La liste des processus rattachés à un *cgroup* se trouve dans le fichier `task`
|
||||
du groupe. Pour ajouter une tâche à ce groupe, cela se passe de cette manière :
|
||||
|
||||
```
|
||||
echo $PID > /sys/fs/cgroup/freezer/virli/tasks
|
||||
```
|
||||
|
||||
Il faut ici remplacer `$PID` par le PID du shell que l'on a relevé juste avant.
|
||||
|
||||
En validant cette commande, vous avez déplacé le processus dans ce groupe, il
|
||||
n'est alors plus dans aucun autre groupe (dans ce *cgroup*, il ne bouge pas
|
||||
dans les autres *cgroup*s).
|
||||
|
||||
### Consultation de l'état
|
||||
|
||||
En affichant le contenu du dossier `virli`, nous avons pu constater que
|
||||
celui-ci contenait déjà un certain nombre de fichiers. Certain d'entre-eux sont
|
||||
en lecture seule et permettent de lire des statistiques instantanées sur le
|
||||
groupe ; tandis que d'autres sont des propriétés que vous pouvez modifier.
|
||||
|
||||
Nous pouvons consulter l'état de gel du groupe en affichant le contenu du
|
||||
fichier\newline `/sys/fs/cgroup/freezer/virli/freezer.state`.
|
||||
|
||||
Pour plus d'information sur les différents fichiers présents dans ce *cgroup*,
|
||||
consulter la documentation, accessible ici :
|
||||
<https://www.kernel.org/doc/Documentation/cgroups/freezer-subsystem.txt>
|
||||
|
||||
### Changement d'état
|
||||
|
||||
Faisons exécuter à notre interpréteur une commande pour voir effectivement
|
||||
l'exécution s'arrêter. Si vous manquez d'inspiration, utilisez :
|
||||
|
||||
```
|
||||
for i in $(seq 9999); do echo -n $i; sleep .1; echo -n " - "; sleep .1; done
|
||||
```
|
||||
|
||||
Maintenant, nous avons donné l'ordre au noyau de ne plus allouer de temps de
|
||||
calcul à notre shell et ses fils :
|
||||
|
||||
```
|
||||
echo FROZEN > /sys/fs/cgroup/freezer/virli/freezer.state
|
||||
```
|
||||
|
||||
À cet instant, vous devriez voir votre compteur s'arrêter. Pour reprendre
|
||||
l'exécution :
|
||||
|
||||
```
|
||||
echo THAWED > /sys/fs/cgroup/freezer/virli/freezer.state
|
||||
```
|
||||
|
||||
|
||||
## Script de monitoring
|
||||
|
||||
À nous maintenant de concevoir un script qui va enregistrer vers la base de
|
||||
données créée (*metrics*) dans la partie précédente, des statistiques issues
|
||||
des *cgroup*s.
|
||||
|
||||
### Monitoring instantané vers la console
|
||||
|
||||
Dans un premier temps, commençons par afficher dans la console la quantité de
|
||||
mémoire utilisée par le groupe monitoré.
|
||||
|
||||
* Arguments de la ligne de commande :
|
||||
- premier fils à lancer dans le groupe,
|
||||
- intervalle de temps entre deux rafraîchissement ;
|
||||
* *cgroup* `memory`;
|
||||
* `memory.usage_in_bytes`.
|
||||
|
||||
Vous pouvez utiliser un programme comme `memhog` pour remplir rapidement votre
|
||||
mémoire.
|
||||
|
||||
Si vous n'avez pas le *cgroup* memory, il est possible qu'il ne soit
|
||||
pas activé par défaut par votre système. Si vous êtes dans ce cas, essayez d'ajouter
|
||||
|
||||
```
|
||||
cgroup_enable=memory
|
||||
```
|
||||
|
||||
### Monitoring vers InfluxDB
|
||||
|
||||
Maintenant, envoyons nos données vers la base
|
||||
<https://influxdb.com/docs/v0.9/guides/writing_data.html> :
|
||||
|
||||
```
|
||||
curl -i -XPOST 'http://172.23.42.2:8086/write?db=metrics' --data-binary \
|
||||
"$my_cgroup_name memory.usage_in_bytes=$(cat .../my_cgroup_name/memory.usage_in_bytes)"
|
||||
```
|
||||
|
||||
Pour vérifier que les données sont bien ajoutées, vous pouvez effectuez la
|
||||
requête suivante dans l'interface web d'InfluxDB :
|
||||
|
||||
```
|
||||
SELECT * from "$my_cgroup_name";
|
||||
```
|
||||
|
||||
### Monitorer davantage de données
|
||||
|
||||
Liste non exhaustive de données à monitorer :
|
||||
|
||||
* Nombre d'IOs effectué ;
|
||||
* nombre d'octets lu/écrit sur les disques ;
|
||||
* temps de calcul utilisé ;
|
||||
* trafic réseau généré ;
|
||||
* ...
|
||||
|
||||
<https://www.kernel.org/doc/Documentation/cgroups/>
|
||||
|
||||
### Permettre à l'utilisateur de monitorer des process
|
||||
|
||||
Maintenant, séparer votre script en deux parties afin qu'un utilisateur normal
|
||||
(non-root) puisse utiliser la partie monitoring de notre script.
|
||||
|
||||
Un premier script doit s'occuper de créer le(s) *cgroup*s et lui attribuer les
|
||||
bons droits, tandis que le deuxième va utiliser effectuer le monitoring, sans
|
||||
|
||||
#### Exemple
|
||||
|
||||
```
|
||||
42sh# ./monitor_init my_cgroup_name
|
||||
42sh$ ./monitor my_cgroup_name memhog 500
|
||||
```
|
||||
|
||||
|
||||
## Rendu
|
||||
|
||||
### Script de monitoring
|
||||
|
||||
Rendez la révision la plus avancée de vos scripts de monitoring de process via
|
||||
les *cgroup*s.
|
||||
|
||||
### Questions
|
||||
|
||||
1. Un même processus peut-il être dans plusieurs *cgroup*s de type différents
|
||||
(freezer et cpuacct par exemple) ?
|
||||
|
||||
1. Que sera-t-il possible de limiter via un nouveau *cgroup* dans la prochaine
|
||||
version du noyau (4.3) ?
|
||||
|
||||
1. Actuellement, comment peut-on limiter le nombre de processus lancés par un
|
||||
utilisateur ou un groupe ?
|
||||
47
tutorial/1/cleaning.md
Normal file
47
tutorial/1/cleaning.md
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
\newpage
|
||||
|
||||
Faire le ménage
|
||||
===============
|
||||
|
||||
Au fur et à mesure de vos tests, la taille utilisée par les données de Docker
|
||||
peut devenir conséquente et son interface peut commencer à déborder
|
||||
d'informations dépréciées.
|
||||
|
||||
Dans la mesure du possible, Docker essaie de ne pas encombrer inutilement votre
|
||||
disque dur avec les vieilles images qui ne sont plus utilisées. Il ne va
|
||||
cependant jamais supprimer une image encore lié à un conteneur, ni les
|
||||
conteneurs qui n'auront pas été démarrés avec `--rm`.
|
||||
|
||||
## Conteneurs
|
||||
|
||||
Vous pouvez afficher l'ensemble des conteneurs, quelque soit leur état (en
|
||||
cours d'exécution, arrêté, ...) avec la commande suivante :
|
||||
|
||||
```
|
||||
42sh$ docker ps -a
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS NAMES
|
||||
552d71619723 hello-world "/hello" 4 days ago Exited (0) 4 days ago dreamy_gates
|
||||
0e8bbff6d500 debian "/bin/bash" 2 weeks ago Exited (0) 2 weeks ago cranky_jones
|
||||
```
|
||||
|
||||
IL y a de fortes chances pour que vous n'ayez plus besoin de ces vieux
|
||||
conteneurs. Pour les supprimer, utilisez la commande :
|
||||
|
||||
```
|
||||
docker rm 0e8bbff6d500 552d71619723
|
||||
```
|
||||
|
||||
ou encore :
|
||||
|
||||
```
|
||||
docker rm cranky_jones dreamy_gates
|
||||
```
|
||||
|
||||
|
||||
## Images
|
||||
|
||||
Les vieilles images qui n'ont plus de références sur elles (ni tag, ni
|
||||
conteneur lié) sont automatiques supprimées. Vous n'avez généralement pas à
|
||||
vous occuper de faire du nettoyage dans les images. Néanmoins, vous pouvez les
|
||||
lister avec la commande `docker images` et en supprimer grâce à la commande
|
||||
`docker rmi`.
|
||||
170
tutorial/1/dockerfile.md
Normal file
170
tutorial/1/dockerfile.md
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
\newpage
|
||||
|
||||
`Dockerfile`
|
||||
============
|
||||
|
||||
## Mon premier conteneur ... par `Dockerfile`
|
||||
|
||||
Pour construire une image, vous n'êtes pas obligé de passer par une série de
|
||||
commits. Docker dispose d'un mécanisme permettant d'automatiser la construction
|
||||
de nouvelles images. Vous pouvez arriver au même résultat que ce que l'on a
|
||||
réussi à faire précédemment en utilisant le Docker file suivant :
|
||||
|
||||
```
|
||||
FROM ubuntu:latest
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y nano
|
||||
```
|
||||
|
||||
La syntaxe d'un `Dockerfile` est simple, le premier mot de chaque ligne est
|
||||
l'intitulé d'une instruction (que l'on écrit généralement en majuscule), elle
|
||||
est suivie de ses arguments.
|
||||
|
||||
Dans notre exemple, nous utilisons `FROM` qui indique une image de départ à
|
||||
utiliser ; `RUN` est une commande qui sera exécutée dans le conteneur, dans le
|
||||
but de le construire.
|
||||
|
||||
Pour lancer la construction de la nouvelle image, créer un nouveau dossier ne
|
||||
contenant que votre fichier `Dockerfile`, placez-vous dedans, puis utilisez la
|
||||
commande `build` :
|
||||
|
||||
```
|
||||
docker build --tag=my_editor .
|
||||
```
|
||||
|
||||
Une fois la construction de l'image terminée, vous pouvez la lancer et
|
||||
constater l'existence de notre éditeur favori :
|
||||
|
||||
```
|
||||
docker run -it my_editor /bin/bash
|
||||
```
|
||||
|
||||
|
||||
## `RUN` dans le `Dockerfile`
|
||||
|
||||
Dans un `Dockerfile`, chaque ligne est exécutée indépendamment des
|
||||
autres et correspondra à une nouvelle couche de notre image.
|
||||
|
||||
Cela signifie que l'exemple suivant **ne fonctionne pas** :
|
||||
|
||||
```
|
||||
COPY db.sql /db.sql
|
||||
RUN service mysqld start
|
||||
RUN mysql -u root -p toor virli < /db.sql
|
||||
```
|
||||
|
||||
Cet exemple ne fonctionne pas car le serveur MySQL est lancé dans le premier
|
||||
RUN, n'est plus lancé au moment du deuxième RUN. En effet, chaque commande du
|
||||
`Dockerfile` a pour but de modifier le système de fichiers.
|
||||
|
||||
Pour avoir le résultat escompté, il faut exécuter les commandes ensemble :
|
||||
|
||||
```
|
||||
COPY db.sql /db.sql
|
||||
RUN service mysqld start && mysql -u root -p toor virli < /db.sql
|
||||
```
|
||||
|
||||
Après le `RUN`, MySQL sera de nouveau arrêté, si on veut l'utiliser dans le
|
||||
conteneur, il ne faudra pas oublier de lancer le processus.
|
||||
|
||||
|
||||
## Exposer des ports
|
||||
|
||||
Construisons maintenant un conteneur avec un serveur web :
|
||||
|
||||
```
|
||||
FROM my_editor
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y nginx
|
||||
|
||||
EXPOSE 80
|
||||
```
|
||||
|
||||
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
|
||||
conteneur :
|
||||
|
||||
```
|
||||
docker build --tag=my_webserver .
|
||||
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` qui sera
|
||||
exécutée si aucune commande n'est passée lors du `run`, par exemple :
|
||||
|
||||
```
|
||||
CMD nginx -g "daemon off;"
|
||||
```
|
||||
|
||||
```
|
||||
docker build --tag=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`.
|
||||
|
||||
|
||||
## D'autres instructions ?
|
||||
|
||||
Consultez <https://docs.docker.com/reference/builder/> pour la liste complète
|
||||
des instructions reconnues.
|
||||
|
||||
|
||||
## Rendu
|
||||
|
||||
### Exercice
|
||||
|
||||
Rendez le fichier `Dockerfile` et son contexte (`index.html`, fichiers de conf
|
||||
éventuels, ...) que vous avez utiliser pour réaliser votre image
|
||||
`my_webserver`.
|
||||
|
||||
### Questions
|
||||
|
||||
1. De combien de couches de systèmes de fichiers est composé votre image
|
||||
`my_webserver` ? Comment pourriez-vous en avoir moins ?
|
||||
|
||||
1. On a vu comment créer une nouvelle image à partir d'une image existante
|
||||
(`FROM`). Mais comment sont créés les images de bases comme debian ou ubuntu
|
||||
(quelle(s) commande(s) et quels type(s) de fichier(s)) ?
|
||||
|
||||
1. Quels sont les avantages de ce `RUN` :
|
||||
|
||||
```
|
||||
RUN apt-get update && \
|
||||
apt-get install -y \
|
||||
nginx \
|
||||
php5-fpm \
|
||||
php5-mysql \
|
||||
php5-gd \
|
||||
&& apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
```
|
||||
|
||||
par rapport aux précédents exemples :
|
||||
|
||||
```
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y nginx php5-fpm
|
||||
```
|
||||
145
tutorial/1/first.md
Normal file
145
tutorial/1/first.md
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
\newpage
|
||||
|
||||
Mon premier conteneur
|
||||
=====================
|
||||
|
||||
Afin de tester la bonne marche de votre installation, exécutons la commande :
|
||||
|
||||
```
|
||||
docker run hello-world
|
||||
```
|
||||
|
||||
Cette commande va automatiquement exécuter une série de commandes pour nous,
|
||||
comme indiqué dans le message affiché en retour :
|
||||
|
||||
D'abord, le daemon va rechercher s'il possède localement l'image
|
||||
*hello-world*. Si ce n'est pas le cas, il va aller la récupérer sur
|
||||
<hub.docker.com>. Ce site met à disposition un grand nombre d'images : des
|
||||
systèmes de base comme Ubuntu, Debian, Centos, etc. jusqu'à des conteneurs
|
||||
prêts à l'emploi : le serveur web nginx, la base de données MySQL, un serveur
|
||||
node.js, etc.
|
||||
|
||||
Nous pouvons directement utiliser le client pour rechercher une image sur le
|
||||
hub, en utilisant la commande `search` :
|
||||
|
||||
```
|
||||
docker search mariadb
|
||||
```
|
||||
|
||||
Il est possible de mettre à jour les images locales ou simplement
|
||||
pré-télécharger des images depuis le hub en utilisant la commande `pull` :
|
||||
|
||||
```
|
||||
docker pull ubuntu
|
||||
```
|
||||
|
||||
Pour consulter la liste des images dont nous disposons localement (soit parce
|
||||
qu'on les a téléchargées, soit parce que nous les avons créées nous-même), on
|
||||
utilise la commande `images` :
|
||||
|
||||
```
|
||||
docker images
|
||||
```
|
||||
|
||||
Nous devrions constater la présence de deux images « Ubuntu », ayant un *TAG*
|
||||
différent. Souvent, il existe plusieurs versions d'une même image. Pour Ubuntu
|
||||
par exemple, nous avons la possibilité de lancer la version `vivid`, `trusty`
|
||||
ou `precise`.
|
||||
|
||||
Chaque image est identifiable par son *Image ID* unique ; les noms d'images
|
||||
ainsi que leurs tags sont, comme les tags Git, une manière humainement plus
|
||||
simple de faire référence aux identifiants.
|
||||
|
||||
Chaque nom d'image possède au moins un tag associé : *latest*. C'est le tag qui
|
||||
est automatiquement recherché lorsque l'on ne le précisez pas en lançant
|
||||
l'image.
|
||||
|
||||
|
||||
## Exécuter un programme dans un conteneur
|
||||
|
||||
Maintenant que nous avons à notre disposition l'image d'un conteneur Ubuntu,
|
||||
lançons-la !
|
||||
|
||||
La commande `run` de Docker prend comme derniers arguments le programme à
|
||||
lancer dans le conteneur ainsi que ses éventuels arguments. Essayons d'afficher
|
||||
un Hello World :
|
||||
|
||||
```
|
||||
docker run ubuntu /bin/echo "Hello World"
|
||||
```
|
||||
|
||||
Dans notre exemple, c'est bien le `/bin/echo` présent dans le conteneur qui est
|
||||
appelé. Ce n'est pas le programme `/bin/echo` de la machine hôte qui a été
|
||||
transféré dans le conteneur.
|
||||
|
||||
|
||||
## Modifier un conteneur
|
||||
|
||||
À chaque fois que nous lançons un `run`, un nouveau conteneur est créé à partir
|
||||
de l'image que l'on a précisé (via un mécanisme de Copy-On-Write, c'est donc
|
||||
très rapide et ne consomme pas beaucoup d'espace disque). Cela signifie que
|
||||
lorsque nous exécutons une commande modifiant les fichiers d'un conteneur, cela
|
||||
ne modifie pas l'image de base, mais crée une nouvelle image. Que nous pouvons
|
||||
ensuite utiliser comme image de base.
|
||||
|
||||
Commençons par entrer dans un nouveau conteneur pour modifier l'image :
|
||||
|
||||
```
|
||||
docker run -it ubuntu /bin/bash
|
||||
```
|
||||
|
||||
Nous voilà maintenant dans le conteneur ! Il est assez épuré, il n'y a rien de
|
||||
superflu : il n'y a même pas d'éditeur de texte : ni vim, ni emacs, même pas
|
||||
`vi` !
|
||||
|
||||
La première chose à faire est de télécharger la liste des paquets. En effet,
|
||||
afin de ne pas livrer de superflu, la liste des paquets et son cache ne sont
|
||||
pas inclues dans le conteneur.
|
||||
|
||||
```
|
||||
apt-get update
|
||||
```
|
||||
|
||||
Il peut arriver que des paquets présents dans l'image ne soient pas à
|
||||
jour. Afin de garder un environnement cohérent, il est recommandé de ne pas
|
||||
utiliser le gestionnaire de paquets pour mettre à jour les paquets présent de
|
||||
base, mais plutôt de contacter le mainteneur de l'image pour qu'il la mette à
|
||||
jour.
|
||||
|
||||
Installons maintenant un programme :
|
||||
|
||||
```
|
||||
apt-get install nano
|
||||
```
|
||||
|
||||
En attendant la fin de l'installation, jetons un œil à la commande dans un
|
||||
autre terminal :
|
||||
|
||||
```
|
||||
docker ps
|
||||
```
|
||||
|
||||
Cette commande liste les conteneurs actifs. Notez le *Container ID* ainsi que
|
||||
le *NAMES* du conteneur du conteneur actuellement en cours d'installation de
|
||||
`nano`.
|
||||
|
||||
Lorsque l'installation de `nano` est terminée, quittez l'image en tapant
|
||||
`exit`.
|
||||
|
||||
Sauvegardez votre image modifiée avec la commande `commit` pour pouvoir
|
||||
commencer directement de votre image avec `nano` :
|
||||
|
||||
```
|
||||
docker commit CONTAINER my_nano
|
||||
```
|
||||
|
||||
En remplaçant `CONTAINER` par le nom ou l'identifiant de votre
|
||||
container. `my_nano` est le nom que vous voudrez utiliser à la place
|
||||
d'`ubuntu` :
|
||||
|
||||
```
|
||||
docker run -it my_nano /bin/bash
|
||||
```
|
||||
|
||||
Vous constatez cette fois que vous pouvez lancer `nano`, alors que vous ne
|
||||
pouvez toujours pas le faire dans un conteneur issue d'une image `ubuntu` !
|
||||
|
|
@ -1,55 +1,107 @@
|
|||
\newpage
|
||||
|
||||
# Installation
|
||||
Installation
|
||||
============
|
||||
|
||||
## Noyau Linux
|
||||
## Prérequis
|
||||
|
||||
Ce TP requiert un noyau Linux 3.8 au minimum. De plus, il doit être
|
||||
compilé avec les options suivantes :
|
||||
Docker repose sur plusieurs techniques implémentés dans les récents noyaux
|
||||
Linux. Nous consacrerons les prochains cours à comprendre leurs
|
||||
fonctionnement. Ces techniques ne sont pas limitées à une architecture de
|
||||
microprocesseur spécifique (comme peuvent l'être les instructions de
|
||||
virtualisation nécessaire pour rendre les hyperviseurs attractifs) ; cependant
|
||||
la communauté autour de Docker utilisant principalement l'architecture `amd64`,
|
||||
c'est sur cette dernière que Docker pourra être exploité à son plein potentiel.
|
||||
|
||||
Avant de continuer, assurez-vous que votre machine a bien démarré sur un noyau
|
||||
64 bits. Le retour de la commande `uname -m` doit vous indiquer :
|
||||
|
||||
```
|
||||
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
|
||||
[*] 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
|
||||
<M> 802.1Q VLAN Support
|
||||
[*] Network priority cgroup
|
||||
[*] Network classid cgroup
|
||||
Device Drivers --->
|
||||
[*] Network device support --->
|
||||
<M> MAC-VLAN support
|
||||
<*> Virtual ethernet pair device
|
||||
Character devices --->
|
||||
-*- Unix98 PTY support
|
||||
[*] Support multiple instances of devpts
|
||||
x86_64
|
||||
```
|
||||
|
||||
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`.
|
||||
Assurez-vous également d'avoir un noyau récent, avec la commande `uname -r` :
|
||||
|
||||
```
|
||||
4.7.2-gentoo
|
||||
```
|
||||
|
||||
Vous ne pourrez pas utiliser Docker avec un noyau antérieur à la version 3.10.
|
||||
|
||||
|
||||
## LXC
|
||||
## Par le gestionnaire de paquets
|
||||
|
||||
Pour installer LXC, utilisez le gestionnaire de paquets de votre
|
||||
distribution. Toute les bonnes distributions fournissent un paquet
|
||||
`lxc`. Vérifiez que la version installée est au moins la 1.0.
|
||||
En général, votre distribution mettra à votre disposition une version de Docker
|
||||
plus ou moins récente. Sous Debian et ses dérivés (Ubuntu, Mint, ...) le paquet
|
||||
a été nommé `docker.io`.
|
||||
|
||||
Aucune configuration ne devrait vous être demandé durant l'installation. Une
|
||||
fois installé, exécutez la commande `lxc-checkconfig` pour vérifier que votre
|
||||
noyau possède bien toutes les options nécessaires.
|
||||
Si dans un environnement de production, on préférera sans doute utiliser une
|
||||
version déjà bien éprouvée, pour ce cours, nous allons avoir besoin de la
|
||||
dernière version disponible :
|
||||
|
||||
<https://docs.docker.com/engine/installation/linux/>
|
||||
|
||||
Installez également le paquet `docker-compose`.
|
||||
|
||||
|
||||
## Vérifier la bonne marche de l'installation
|
||||
|
||||
Vous devriez maintenant être capable de lancer la commande suivante :
|
||||
|
||||
```
|
||||
docker version
|
||||
```
|
||||
|
||||
Une sortie similaire au bloc suivant devrait apparaître sur votre écran :
|
||||
|
||||
```
|
||||
Client:
|
||||
Version: 1.12.1
|
||||
API version: 1.24
|
||||
Go version: go1.7
|
||||
Git commit: 23cf638
|
||||
Built:
|
||||
OS/Arch: linux/amd64
|
||||
|
||||
Server:
|
||||
Version: 1.12.1
|
||||
API version: 1.24
|
||||
Go version: go1.7
|
||||
Git commit: 23cf638
|
||||
Built:
|
||||
OS/Arch: linux/amd64
|
||||
```
|
||||
|
||||
### `no such file or directory`?
|
||||
|
||||
Si vous avez cette erreur : `dial unix /var/run/docker.sock: no such file or
|
||||
directory.`, le deamon n'est sans doute pas lancé. Lancez-le :
|
||||
|
||||
```
|
||||
sudo service docker restart
|
||||
```
|
||||
|
||||
|
||||
### `permission denied`?
|
||||
|
||||
Si vous avez cette erreur : `dial unix /var/run/docker.sock: permission
|
||||
denied.`, ajoutez votre utilisateur au groupe `docker` et **relancer votre
|
||||
session** :
|
||||
|
||||
```
|
||||
sudo gpasswd -a $USER docker
|
||||
```
|
||||
|
||||
**Attention :** cette action n'est pas anodine d'un point de vue sécurité :
|
||||
<https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface>
|
||||
|
||||
|
||||
## Rendu
|
||||
|
||||
### Questions
|
||||
|
||||
1. Dans quel langage Docker a-t-il été écrit ?
|
||||
|
||||
1. Décrivez une méthode permettant à un utilisateur (non-root), présent dans le
|
||||
groupe `docker`, d'effectuer une action privilégiée impactant la machine
|
||||
hôte.
|
||||
|
|
|
|||
36
tutorial/1/linking.md
Normal file
36
tutorial/1/linking.md
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
\newpage
|
||||
|
||||
Lier les conteneurs
|
||||
===================
|
||||
|
||||
En plus de vouloir partager des répertoires entre deux conteneurs, il est
|
||||
souvent nécessaire de partager des ports.
|
||||
|
||||
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 à
|
||||
<https://docs.docker.com/userguide/dockerlinks/#environment-variables>.
|
||||
|
||||
On utilise 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/>.
|
||||
|
|
@ -1,214 +0,0 @@
|
|||
\newpage
|
||||
|
||||
# Utiliser `lxc`
|
||||
|
||||
Le but de cette première partie est d'appréhender la virtualisation légère au
|
||||
travers d'un programme, `lxc`, qui va mettre en place pour nous tout un
|
||||
environnement distinct.
|
||||
|
||||
|
||||
## Lancer un conteneur
|
||||
|
||||
Avec le paquet `lxc` que nous avons précédemment installé, nous avons également
|
||||
récupéré un certain nombre de *modèles* de système (souvent installés dans le
|
||||
dossier `/usr/share/lxc/templates/`) : il s'agit d'une suite de commandes
|
||||
(principalement des `wget`, `chroot` ou `debootstrap`) permettant d'obtenir un
|
||||
système basic fonctionnel, en suivant les étapes d'installation habituelle de
|
||||
la distribution.
|
||||
|
||||
La méthode la plus simple pour lancer un conteneur `lxc` est d'utiliser l'un de
|
||||
ces modèles pour obtenir un nouveau système. On utilise pour cela la commande
|
||||
`lxc-create` :
|
||||
|
||||
```
|
||||
lxc-create --name toto_first --template debian
|
||||
```
|
||||
|
||||
Ce modèle va créer un dossier dans `/var/lib/lxc/` (pouvant varier d'une
|
||||
distribution à l'autre) portant le nom que nous avons précisé. Ce dossier va
|
||||
contenir la configuration `lxc` du conteneur (`config`), la table des
|
||||
partitions (`fstab`) s'il y a besoin de faire des montages particuliers et
|
||||
enfin le dossier `rootfs` contenant le système en lui-même.
|
||||
|
||||
Une fois l'installation terminée, on peut démarrer le conteneur :
|
||||
|
||||
```
|
||||
lxc-start --name toto_first
|
||||
```
|
||||
|
||||
`lxc` va appeler `/sbin/init` et démarrer tous les services que l'on peut
|
||||
s'attendre à trouver dans n'importe quelle machine virtuelle (et même physique)
|
||||
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`, car on n'a pas
|
||||
vraiment envie d'avoir un conteneur bloquant un terminal. En mode daemon, on va
|
||||
utiliser la commande `lxc-console` pour nous attacher aux conteneurs. À tout
|
||||
moment, nous pouvons nous détacher de la console (sans que cela n'affecte
|
||||
l'état du conteneur) en pressant les touches : `^A q`.
|
||||
|
||||
Connectons-nous, lancons quelques commandes puis éteignons la machine en
|
||||
lançant la commande `poweroff` dans le conteneur. Il est également possible de
|
||||
lancer la commande `lxc-stop --name toto_first` dans un autre terminal, depuis
|
||||
la machine hôte.
|
||||
|
||||
|
||||
## Le réseau
|
||||
|
||||
Le modèle *Debian*, que nous avons utilisé, préremplit un fichier de
|
||||
configuration sans définir de paramètre pour le réseau. Il n'y a donc pas
|
||||
d'interface dans le conteneur pour le connecter :
|
||||
|
||||
```
|
||||
lxc.network.type = empty
|
||||
```
|
||||
|
||||
Un excellent article détaillant les différents types de configuration 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.
|
||||
|
||||
### MACVLAN
|
||||
|
||||
Cette méthode est la plus simple : le noyau va orienter les paquets en fonction
|
||||
de leur adresse MAC de destination. Le conteneur sera donc comme une machine
|
||||
supplémentaire sur le réseau.
|
||||
|
||||
Modifions notre fichier de configuration afin qu'il ressemble à quelque chose
|
||||
comme :
|
||||
|
||||
```
|
||||
lxc.network.type = macvlan
|
||||
lxc.network.macvlan.mode = bridge
|
||||
lxc.network.flags = up
|
||||
lxc.network.link = eth0
|
||||
```
|
||||
|
||||
Après avoir démarré le conteneur, il devrait avoir obtenu une IP du serveur
|
||||
DHCP de l'école. L'inconvénient dans cette configuration est qu'il faille un
|
||||
client netsoul dans chaque conteneur, puisque chacun est considéré comme une
|
||||
machine différente aux yeux du routeur.
|
||||
|
||||
|
||||
### Virtual Ethernet
|
||||
|
||||
Virtual Ethernet est la configuration la moins optimale, mais sans doute la
|
||||
plus flexible.
|
||||
|
||||
Voici un extrait de configuration correspondant au paramétrage d'une interface
|
||||
virtuelle pour un conteneur donné :
|
||||
|
||||
```
|
||||
lxc.network.type = veth
|
||||
lxc.network.ipv4 = 172.23.42.2/24
|
||||
lxc.network.flags = up
|
||||
```
|
||||
|
||||
Dans cette situation, au démarrage du conteneur, `lxc` va créer une interface
|
||||
veth, avec un côté placé dans la machine hôte et l'autre côté placé dans le
|
||||
conteneur. `lxc` configure l'interface dans le conteneur, il nous appartient
|
||||
ensuite de configurer la machine hôte.
|
||||
|
||||
Commençons par attribuer une IP à cette nouvelle interface, en adaptant à votre
|
||||
identifiant d'interface :
|
||||
|
||||
```
|
||||
ip addr add 172.23.42.1/24 dev vethYJWD6R
|
||||
```
|
||||
|
||||
À partir de là, nous devrions pouvoir pinger notre conteneur depuis notre
|
||||
machine hôte : `ping 172.23.42.2`.
|
||||
|
||||
Notre conteneur ne peut cependant pas encore accéder à Internet. Pour cela, la
|
||||
machine hôte doit faire office de routeur et donc router les paquets d'un
|
||||
réseau à l'autre : en l'occurence, du réseau 172.23.42.1 vers Internet
|
||||
via 10.0.0.0/8, le réseau de l'école.
|
||||
|
||||
Pour que notre machine hôte route les paquets, exécuter la commande :
|
||||
|
||||
```
|
||||
sysctl -w net.ipv4.ip_forward=1
|
||||
```
|
||||
|
||||
Cette variable, que nous retrouvons dans `/proc/sys/net/ipv4/ip_forward`,
|
||||
indique au noyau qu'il peut faire passer les paquets réseau d'une interface à
|
||||
l'autre. Sans plus de directives, les paquets vont conserver leur adresse
|
||||
source (172.23.42.2 pour les paquets en provenance du conteneur). Cette adresse
|
||||
est une adresse privée, non routable sur Internet, ni même par le bocal. Il
|
||||
faut donc ajouter une couche de NAT/PAT pour réécrire les adresses sources
|
||||
avant d'envoyer les paquets sur internet :
|
||||
|
||||
```
|
||||
iptables -t nat -A POSTROUTING ! -o vethYJWD6R -s 172.23.42.0/24 -j MASQUERADE
|
||||
```
|
||||
|
||||
Dernière étape, dans notre conteneur, nous devons indiquer la route à utiliser
|
||||
pour accéder à internet :
|
||||
|
||||
```
|
||||
ip route add default via 172.23.42.1
|
||||
```
|
||||
|
||||
Nous avons maintenant internet dans notre conteneur !
|
||||
|
||||
|
||||
## Utilisation du conteneur
|
||||
|
||||
### Installation de InfluxDB
|
||||
|
||||
```
|
||||
apt-get update
|
||||
apt-get install wget
|
||||
wget https://s3.amazonaws.com/influxdb/influxdb_0.9.4.2_amd64.deb
|
||||
dpkg -i influxdb_0.9.4.2_amd64.deb
|
||||
```
|
||||
|
||||
### Test de l'installation
|
||||
|
||||
```
|
||||
/opt/influxdb/influxd
|
||||
```
|
||||
|
||||
Une fois que le service est démarré, vous devriez pouvoir accéder à l'interface
|
||||
à : <http://172.23.42.2:8083/>
|
||||
|
||||
Créons une nouvelle base de données "metrics", elle nous servira dans la partie suivante.
|
||||
|
||||
|
||||
## Rendu
|
||||
|
||||
### Configuration du conteneur
|
||||
|
||||
En plus des modifications que vous avez effectuées durant le TP, modifiez la
|
||||
configuration du conteneur afin qu'il ne puisse pas utiliser plus que 256 MB de
|
||||
RAM et 512 MB de swap.
|
||||
|
||||
Limitez ensuite les `capabilities(7)` de ce conteneur afin qu'il s'exécute avec
|
||||
le strict minimum de droits, nécessaire au bon fonctionnement des programmes
|
||||
installés.
|
||||
|
||||
**Rendez le fichier `config` de ce premier conteneur.** N'hésitez pas à laisser
|
||||
des commentaires justifiant vos éventuels choix.
|
||||
|
||||
### Questions
|
||||
|
||||
1. Quels sont les autres types de virtualisation réseau existants ? Expliquez
|
||||
en chacun une phrase leurs particularités.
|
||||
|
||||
1. Quel fichier de configuration devriez-vous changer pour rendre persistante la
|
||||
valeur d'`ip_forward` ?
|
||||
|
||||
1. Dans quel langage InfluxDB a-t-il été écrit ? Quelle est la particularité
|
||||
des binaires générés par ce langage ?
|
||||
|
||||
1. Quels sont les avantages et les inconvénients associés au linkage statique
|
||||
et au linkage dynamique ? (pas forcément que dans le cadre de la
|
||||
virtualisation légère).
|
||||
|
||||
1. J'ai utilisé la méthode *Virtual Ethernet* pour relier mes conteneurs à
|
||||
Internet, via l'interface `br0`. Quelle(s) règle(s) `iptables` devrais-je
|
||||
écrire sur mon hôte afin de permettre l'accès à InfluxDB depuis une autre
|
||||
machine ?
|
||||
|
|
@ -1,133 +0,0 @@
|
|||
\newpage
|
||||
|
||||
# Utiliser les *namespaces*
|
||||
|
||||
## Comparaison de *namespace*
|
||||
|
||||
Écrivez un script ou un programme, `cmpns`, dans le langage courant de votre
|
||||
choix, permettant de déterminer si deux programmes s'exécutent dans les mêmes
|
||||
*namespaces*.
|
||||
|
||||
### Exemples
|
||||
|
||||
```sh
|
||||
42sh$ cmpns $(pgrep influxdb) $(pgrep init)
|
||||
- ipc: differ
|
||||
- mnt: differ
|
||||
- net: differ
|
||||
- pid: differ
|
||||
- user: same
|
||||
- uts: same
|
||||
```
|
||||
|
||||
```sh
|
||||
42sh$ cmpns $(pgrep init) self
|
||||
- ipc: same
|
||||
- mnt: same
|
||||
- net: same
|
||||
- pid: same
|
||||
- user: same
|
||||
- uts: same
|
||||
```
|
||||
|
||||
Ici, `self` fait référence au processus actuellement exécuté.
|
||||
|
||||
|
||||
## Rejoindre un *namespace*
|
||||
|
||||
Dans le langage courant de votre choix, écrivez un programme : `setns`,
|
||||
permettant, à la manière de `unshare(1)` et `unshare(2)`, d'utiliser `setns(2)`
|
||||
via votre interpréteur.
|
||||
|
||||
Les options attendues sont :
|
||||
|
||||
* rejoindre un *namespace* IPC : `-i`, `--ipc` ;
|
||||
* rejoindre un *namespace* mount : `-m`, `--mount` ;
|
||||
* rejoindre un *namespace* net : `-n`, `--net` ;
|
||||
* rejoindre un *namespace* PID : `-p`, `--pid` ;
|
||||
* rejoindre un *namespace* UTS : `-u`, `--uts` ;
|
||||
* rejoindre un *namespace* user : `-U`, `--user`.
|
||||
|
||||
### Exemples
|
||||
|
||||
```sh
|
||||
42sh# setns /bin/bash
|
||||
bash# _
|
||||
```
|
||||
|
||||
#### IPC and PID Namespaces
|
||||
|
||||
```sh
|
||||
42sh# setns --ipc=/proc/42/ns/ipc -p /proc/42/ns/pid /bin/echo toto
|
||||
toto
|
||||
```
|
||||
|
||||
#### Net Namespace
|
||||
|
||||
```sh
|
||||
42sh# setns --net=/proc/42/ns/net ip a
|
||||
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default
|
||||
link/loopback 00:00:00:00:00:00 brd 00:00:00:00::00
|
||||
inet 127.0.0.1/8 brd 127.255.255.255 scope lo
|
||||
valid_lft forever preferred_lft
|
||||
inet6 ::1/128 scope host
|
||||
valid_lft forever preferred_lft forever
|
||||
```
|
||||
|
||||
#### UTS Namespace
|
||||
|
||||
```sh
|
||||
42sh# hostname --fqdn
|
||||
koala.zoo.paris
|
||||
42sh# setns --uts=/proc/42/ns/uts hostname --fqdn
|
||||
lynx.zoo.paris
|
||||
```
|
||||
|
||||
|
||||
## My Little Container
|
||||
|
||||
En utilisant le langage courant de votre choix, concevez l'exécutable `mlc`,
|
||||
permettant de lancer une application dans un environnement différent (comme un
|
||||
`chroot`, mais sans permettre de s'en échapper) et avec des privilèges réduits.
|
||||
|
||||
Votre solution doit créer au moins un nouveau *namespace* mount et PID.
|
||||
|
||||
Vous aurez sans doute besoin de : `clone(2)`, `capabilities(7)`, `capset(2)`, `pivot_root(2)`,
|
||||
|
||||
### Exemples
|
||||
|
||||
```sh
|
||||
42sh# ls newroot
|
||||
bin etc home usr root proc var
|
||||
|
||||
42sh# mlc newroot/ /bin/bash
|
||||
bash# ls ../../../
|
||||
bin etc home usr root proc var
|
||||
|
||||
bash# escape_chroot ls
|
||||
bin etc home usr root proc var
|
||||
|
||||
bash# ls -ld /proc/[0-9]* | wc -l
|
||||
2
|
||||
|
||||
bash# curl http://www.linuxcontainers.org/ | md5sum
|
||||
0123456789abcdef
|
||||
|
||||
bash# ping 8.8.8.8
|
||||
Operation not permitted
|
||||
```
|
||||
|
||||
|
||||
## Rendu
|
||||
|
||||
Pour chaque exercice de cette partie, vous pouvez rendre un seul fichier s'il
|
||||
s'agit d'un script ; sinon, vous devez rendre une tarball contenant un
|
||||
`Makefile` permettant de générer les éventuels exécutables et/ou un `README`
|
||||
expliquant comment s'en servir.
|
||||
|
||||
Vous devez donc rendre 3 fichiers : `cmpns` ou `cmpns.tar.bz2`, `setns` ou
|
||||
`setns.tar.bz2` et `mlc` ou `mlc.tar.bz2`.
|
||||
|
||||
\vspace{3em}
|
||||
|
||||
Bon courage !
|
||||
114
tutorial/1/rendu.md
Normal file
114
tutorial/1/rendu.md
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
\newpage
|
||||
|
||||
Sujet à rendre
|
||||
==============
|
||||
|
||||
## Projet
|
||||
|
||||
Écrivez un `Dockerfile` pour conteneriser un client netsoul.
|
||||
|
||||
Vous pouvez utiliser le client de votre choix, comme par exemple
|
||||
[tumsoul.py](https://virli.nemunai.re/misc/tumsoul_0.3.3.py).
|
||||
|
||||
Une fois construit, le conteneur doit se lancer comme cela :
|
||||
|
||||
```
|
||||
docker run -e NETSOUL_LOGIN="login_x" -e PASSSOCKS="xnigol42" netsoul
|
||||
```
|
||||
|
||||
Passer la configuration d'un conteneur dans des variables d'environnement est
|
||||
une méthode couramment utilisée. Ces variables sont récupérées et traitées par
|
||||
le script d'ENTRYPOINT. Ce qui permet d'utiliser la ligne de commande pour
|
||||
d'autres choses.
|
||||
|
||||
|
||||
## Modalité de rendu
|
||||
|
||||
Un service automatique s'occupe de réceptionner vos rendus, de faire les
|
||||
vérifications nécessaires et de vous envoyer un accusé de réception (ou de
|
||||
rejet).
|
||||
|
||||
Ce service écoute sur l'adresse <virli@nemunai.re>, c'est donc à cette adresse
|
||||
et exclusivement à celle-ci que vous devez envoyer vos rendus. Tout rendu
|
||||
envoyé à une autre adresse et/ou non signé ne sera pas pris en compte.
|
||||
|
||||
|
||||
## Tarball
|
||||
|
||||
Tous les fichiers identifiés comme étant à rendre pour ce TP sont à
|
||||
placer dans une tarball (pas d'archive ZIP, RAR, ...).
|
||||
|
||||
Les réponses aux questions sont à regrouper dans un fichier `questions.txt` à
|
||||
placer à la racine de votre rendu.
|
||||
|
||||
Voici une arborescence type:
|
||||
|
||||
```
|
||||
login_x-TP1/questions.txt
|
||||
login_x-TP1/webserver
|
||||
login_x-TP1/webserver/Dockerfile
|
||||
login_x-TP1/webserver/index.html
|
||||
login_x-TP1/webserver/datavolume-run.sh
|
||||
login_x-TP1/netsoul
|
||||
login_x-TP1/netsoul/Dockerfile
|
||||
login_x-TP1/netsoul/docker-entrypoint.sh
|
||||
login_x-TP1/netsoul/tumsoul.py
|
||||
```
|
||||
|
||||
## Signature du rendu
|
||||
|
||||
Deux méthodes sont utilisables pour signer votre rendu :
|
||||
|
||||
* signature du courriel ;
|
||||
* signature de la tarball.
|
||||
|
||||
Dans les deux cas, si vous n'en avez pas déjà une, vous devrez créer une clef
|
||||
PGP à votre nom et prénom.
|
||||
|
||||
Pour valider la signature, il est nécessaire d'avoir reçu la clef publique
|
||||
séparément. Vous avez le choix de l'uploader sur un serveur de clefs, soit de
|
||||
me fournir votre clef en main propre, soit l'envoyer dans un courriel distinct.
|
||||
|
||||
### Signature du courriel
|
||||
|
||||
[Enigmail](https://enigmail.net) est une extension très bien réputée pour
|
||||
signer ses mails depuis Thunderbird.
|
||||
|
||||
Utilisez le service automatique <signcheck@nemunai.re> pour savoir si votre
|
||||
courriel est correctement signé et que je suis en mesure de vérifier la
|
||||
signature.
|
||||
|
||||
|
||||
### Astuces
|
||||
|
||||
#### No public key
|
||||
|
||||
Si vous recevez un rapport avec l'erreur suivante :
|
||||
|
||||
```
|
||||
[FAIL] Bad signature. Here is the gnupg output:
|
||||
|
||||
gpg: Signature made Tue Jan 01 16:42:23 2014 CET
|
||||
gpg: using RSA key 842807A84573CC96
|
||||
gpg: requesting key E2CCD99DD37BD32E from hkp server pool.sks-keyservers.net
|
||||
gpg: Can't check signature: No public key
|
||||
```
|
||||
|
||||
C'est que votre clef publique n'est pas dans mon trousseau et que les méthodes
|
||||
de récupérations automatiques n'ont pas permis de la trouver. Uploadez votre
|
||||
clef sur un serveur de clefs (et attendez quelques minutes sa propagation) ou
|
||||
envoyez un courriel avec votre clef publique en pièce-jointe, avant de retenter
|
||||
votre rendu.
|
||||
|
||||
|
||||
#### Not explicit username
|
||||
|
||||
Si vous recevez un rapport avec l'erreur suivante :
|
||||
|
||||
```
|
||||
[FAIL] The username of your key is not explicit, I can't find you.
|
||||
```
|
||||
|
||||
Votre clef ne contient sans doute pas vos noms et prénoms ou l'adresse
|
||||
électronique associée à la clef n'est pas celle que j'ai dans ma base de
|
||||
données.
|
||||
2
tutorial/1/todo.org
Normal file
2
tutorial/1/todo.org
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
* entrypoint
|
||||
* parler du contexte du build
|
||||
|
|
@ -1,21 +1,27 @@
|
|||
% Virtualisation légère -- TP n^o^ 1
|
||||
% Pierre-Olivier *nemunaire* Mercier
|
||||
% Jeudi 8 octobre 2015
|
||||
---
|
||||
title: Virtualisation légère -- TP n^o^ 1
|
||||
subtitle: Les bases de Docker
|
||||
author: Pierre-Olivier *Nemunaire* Mercier
|
||||
institute: EPITA
|
||||
date: Jeudi 8 septembre 2016
|
||||
...
|
||||
|
||||
Le but de ce premier TP est d'utiliser les commandes et les appels systèmes vu
|
||||
durant le cours.
|
||||
Durant ce premier TP, nous allons apprendre à utiliser Docker !
|
||||
|
||||
Tous les éléments de ce TP (exercices et questions) sont à rendre à
|
||||
<virli@nemunai.re> au plus tard le **mercredi 21 octobre 2015 à 23
|
||||
h 42**. Consultez la dernière section de chaque partie pour plus d'information
|
||||
sur les éléments à rendre. Vous pouvez placer les réponses aux questions dans
|
||||
le corps du courriel ou dans un fichier texte joint.
|
||||
<virli@nemunai.re> au plus tard le jeudi 15 septembre 2016 à 8 h 42. Consultez la
|
||||
dernière section de chaque partie pour plus d'information sur les éléments à
|
||||
rendre. Vous pouvez placer les réponses aux questions dans le corps du courriel
|
||||
ou dans un fichier joint.
|
||||
|
||||
En tant que personnes sensibilisées à la sécurité des échanges électroniques,
|
||||
vous devriez m'envoyer vos rendus signés avec votre clef PGP. Pensez à
|
||||
[me](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x842807A84573CC96) faire
|
||||
signer votre clef et n'hésitez pas à
|
||||
En tant que personnes sensibilisées à la sécurité des échanges
|
||||
électroniques, vous devrez m'envoyer vos rendus signés avec votre clef
|
||||
PGP. Pensez à
|
||||
[me](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x842807A84573CC96)
|
||||
faire signer votre clef et n'hésitez pas à
|
||||
[faire signer votre clef](http://www.meetup.com/fr/Paris-certification-de-cles-PGP-et-CAcert/).
|
||||
Vous pouvez utiliser l'adresse <signcheck@nemunai.re> pour savoir si
|
||||
vous vous y prenez correctement.
|
||||
|
||||
\hypersetup{linkcolor=black}
|
||||
\tableofcontents
|
||||
|
|
|
|||
115
tutorial/1/volumes.md
Normal file
115
tutorial/1/volumes.md
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
\newpage
|
||||
|
||||
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).
|
||||
|
||||
|
||||
## Partage avec la machine hôte
|
||||
|
||||
Il est possible de monter un répertoire de la machine hôte dans un
|
||||
conteneur. L'intérêt reste plutôt limité à des fins de facilité ou de test, par
|
||||
exemple si vous voulez partager des fichiers avec votre voisin, en passant par
|
||||
le protocole HTTP, mais sans se casser la tête à installer et configurer un
|
||||
serveur web :
|
||||
|
||||
```
|
||||
docker run --rm -p 80:80 -v ~/Downloads:/usr/share/nginx/html:ro -d nginx
|
||||
```
|
||||
|
||||
Une fois cette commande lancée, votre voisin pourra accéder à votre dossier
|
||||
Downloads en renseignant l'IP de votre machine dans son navigateur favori !
|
||||
|
||||
|
||||
## Data Volume Container
|
||||
|
||||
Dans de nombreuses situations, 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.
|
||||
|
||||
|
||||
## Rendu
|
||||
|
||||
### Exercice
|
||||
|
||||
Modifiez le `Dockerfile` de l'exercice précédent pour que votre application web
|
||||
(ok, c'est juste un `index.html` ...) soit contenue dans un *data volume
|
||||
container*.
|
||||
|
||||
Choisissez le nom de votre volume judicieusement, cela peut vous faciliter la
|
||||
vie !
|
||||
|
||||
Écrivez un script shell qui reprend les commandes que vous avez tapé dans votre
|
||||
terminal pour créer le *data volume container*, construire l'image à partir du
|
||||
Dockerfile et lancer le conteneur `my_webserver` lié.
|
||||
|
||||
Rendre le `Dockerfile`, son contexte et le script de construction/lancement.
|
||||
84
tutorial/1/what.md
Normal file
84
tutorial/1/what.md
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
\newpage
|
||||
|
||||
Composition de Docker
|
||||
=====================
|
||||
|
||||
Docker est une suite d'outils de haut niveau, permettant d'utiliser les
|
||||
conteneurs.
|
||||
|
||||
Docker est composé d'un daemon lancé au démarrage de votre machine, avec lequel
|
||||
vous interagissez via un client (le programme `docker`). La communication entre
|
||||
le daemon et le client s'effectuant au travers d'une socket, le client peut ne
|
||||
pas être forcément sur la machine qui exécutera effectivement les conteneurs.
|
||||
|
||||
C'est ce qu'il se passe lorsque vous utilisez Docker4Windows et Docker4Mac :
|
||||
une machine virtuelle Linux est lancé parallèlement à votre système et chaque
|
||||
commande `docker` que vous tapez est passé au deamon dans la machine virtuelle.
|
||||
|
||||
|
||||
## `runc` et `containerd`
|
||||
|
||||
La notion de conteneurs est maintenant normalisées par
|
||||
[l'Open Container Initiative](https://opencontainers.org).
|
||||
|
||||
Docker lance des conteneurs respectant cette norme grâce au programme `runc`.
|
||||
|
||||
Toute la gestion de l'exécution du conteneur est délégué au programme
|
||||
`containerd`, également issue de l'initiative. C'est lui aussi un daemon (géré
|
||||
par Docker), dont le but est de monitorer les conteneurs lancés (pour les
|
||||
relancer en cas de crash par exemple) ou encore de récupérer les logs de chaque
|
||||
conteneur.
|
||||
|
||||
|
||||
## Les images Docker
|
||||
|
||||
Une image Docker est un système de fichiers en lecture seule. Il est formé d'un
|
||||
ensemble de couches, agrégées selon le principe d'UnionFS.
|
||||
|
||||
Une image peut, par exemple, être un système Ubuntu complet ou juste contenir
|
||||
le programme busybox ou encore un serveur web et votre application web, prêt à
|
||||
l'emploi.
|
||||
|
||||
Les images sont utilisées pour créer des conteneurs.
|
||||
|
||||
Il y a deux méthodes pour obtenir des images Docker : soit les construire avec
|
||||
les outils fournis, soit les récupérer depuis un registre.
|
||||
|
||||
|
||||
## Les conteneurs Docker
|
||||
|
||||
Alors que les images constituent la partie immuable de Docker, les conteneurs
|
||||
sont sa partie vivante. Chaque conteneur est créé à partir d'une image : à
|
||||
chaque fois que vous lancez un conteneur, une couche lecture/écriture est
|
||||
ajoutée au dessus de l'image. Cette couche est propre au conteneur et est
|
||||
temporaire : l'image n'est pas modifié par l'exécution d'un conteneur.
|
||||
|
||||
Chaque conteneur s'exécute dans un environnement restreint et distinct de
|
||||
l'environnement principal où vous avez votre bureau. Par exemple, dans cet
|
||||
environnement, vous ne pouvez pas voir les processus qui n'y sont pas.
|
||||
|
||||
|
||||
## Les registres Docker (*Docker registries*)
|
||||
|
||||
Les registres sont des plates-formes de stockage, publiques ou privées,
|
||||
contenant des images. Ils permettent de récupérer des images, mais également
|
||||
d'en réceptionner.
|
||||
|
||||
Le registre utilisé de base est le [Docker Hub](https://hub.docker.com/) : il
|
||||
contient à la fois des images officielles (ubuntu, debian, nginx, ...) et des
|
||||
images crées par des utilisateurs.
|
||||
|
||||
|
||||
## Outils annexes
|
||||
|
||||
En plus du Docker-engine, le daemon et client que nous allons utiliser
|
||||
aujourd'hui, Docker développe également :
|
||||
|
||||
* **Docker-machine :** qui permet d'installer et configurer le daemon
|
||||
rapidement sur plusieurs machines (afin de les utiliser au sein d'un cluster) ;
|
||||
|
||||
* **Docker-swarm :** désormais intégré à Docker (depuis la version 1.12), cela
|
||||
permet de gérer un cluster de machine et de faire de l'orchestration ;
|
||||
|
||||
* **Docker-compose :** qui permet de lancer un ensemble de conteneurs dépend
|
||||
les uns des autres (par exemple un serveur web et sa base de données).
|
||||
Loading…
Add table
Add a link
Reference in a new issue