Ready for j1

This commit is contained in:
nemunaire 2016-09-08 03:44:20 +02:00
parent 150f5a8834
commit dc8544c0c9
31 changed files with 688 additions and 544 deletions

View File

@ -1,13 +1,16 @@
SOURCES = tutorial.md installation.md lxc.md cgroups.md namespaces.md SOURCES = tutorial.md installation.md what.md first.md dockerfile.md volumes.md linking.md cleaning.md rendu.md
TEMPLATE = ../../template.tex
PANDOCOPTS = --latex-engine=xelatex \ PANDOCOPTS = --latex-engine=xelatex \
--standalone \ --standalone \
--normalize \ --normalize \
--number-sections \ --number-sections \
--smart \
-M lang=frenchb \ -M lang=frenchb \
-M fontsize=12pt \ -M fontsize=12pt \
-M papersize=a4paper \ -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 all: tutorial.pdf

47
tutorial/1/cleaning.md Normal file
View 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`.

View File

@ -1,6 +1,7 @@
\newpage \newpage
# `Dockerfile` `Dockerfile`
============
## Mon premier conteneur ... par `Dockerfile` ## Mon premier conteneur ... par `Dockerfile`
@ -42,8 +43,10 @@ docker run -it my_editor /bin/bash
## `RUN` dans le `Dockerfile` ## `RUN` dans le `Dockerfile`
Chaque ligne est exécutée indépendamment des autres ; Dans un `Dockerfile`, chaque ligne est exécutée indépendamment des
cela signifie que l'exemple suivant **ne fonctionne pas** : autres et correspondra à une nouvelle couche de notre image.
Cela signifie que l'exemple suivant **ne fonctionne pas** :
``` ```
COPY db.sql /db.sql COPY db.sql /db.sql

145
tutorial/1/first.md Normal file
View 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` !

View File

@ -1,55 +1,107 @@
\newpage \newpage
# Installation Installation
============
## Noyau Linux ## Prérequis
Ce TP requiert un noyau Linux 3.8 au minimum. De plus, il doit être Docker repose sur plusieurs techniques implémentés dans les récents noyaux
compilé avec les options suivantes : 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 ---> x86_64
[*] 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
``` ```
Une fois que vous aurez installé LXC, vous pouvez vérifier la compatibilité de Assurez-vous également d'avoir un noyau récent, avec la commande `uname -r` :
la configuration de votre noyau en utilisant la commande `lxc-checkconfig`.
```
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 En général, votre distribution mettra à votre disposition une version de Docker
distribution. Toute les bonnes distributions fournissent un paquet plus ou moins récente. Sous Debian et ses dérivés (Ubuntu, Mint, ...) le paquet
`lxc`. Vérifiez que la version installée est au moins la 1.0. a été nommé `docker.io`.
Aucune configuration ne devrait vous être demandé durant l'installation. Une Si dans un environnement de production, on préférera sans doute utiliser une
fois installé, exécutez la commande `lxc-checkconfig` pour vérifier que votre version déjà bien éprouvée, pour ce cours, nous allons avoir besoin de la
noyau possède bien toutes les options nécessaires. 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.

View File

@ -1,6 +1,7 @@
\newpage \newpage
## Lier les conteneurs Lier les conteneurs
===================
En plus de vouloir partager des répertoires entre deux conteneurs, il est En plus de vouloir partager des répertoires entre deux conteneurs, il est
souvent nécessaire de partager des ports. souvent nécessaire de partager des ports.
@ -20,7 +21,7 @@ docker run -e MYSQL_ROOT_PASSWORD=mysecretpassword -d --name db1 mysql
docker run --link db1 my_nginx docker run --link db1 my_nginx
``` ```
### Ambasador ## Ambasador
Afin d'abstraire le plus possible l'infrastructure sous-jacente et d'autoriser Afin d'abstraire le plus possible l'infrastructure sous-jacente et d'autoriser
les migrations de conteneurs, on utilise le modèle *ambassador*. les migrations de conteneurs, on utilise le modèle *ambassador*.

114
tutorial/1/rendu.md Normal file
View 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.

View File

@ -1,21 +1,27 @@
% Virtualisation légère -- TP n^o^ 1 ---
% Pierre-Olivier *nemunaire* Mercier title: Virtualisation légère -- TP n^o^ 1
% Jeudi 8 octobre 2015 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 ce premier TP, nous allons apprendre à utiliser Docker !
durant le cours.
Tous les éléments de ce TP (exercices et questions) sont à rendre à Tous les éléments de ce TP (exercices et questions) sont à rendre à
<virli@nemunai.re> au plus tard le **mercredi 21 octobre 2015 à 23 <virli@nemunai.re> au plus tard le jeudi 15 septembre 2016 à 8 h 42. Consultez la
h 42**. Consultez la dernière section de chaque partie pour plus d'information dernière section de chaque partie pour plus d'information sur les éléments à
sur les éléments à rendre. Vous pouvez placer les réponses aux questions dans rendre. Vous pouvez placer les réponses aux questions dans le corps du courriel
le corps du courriel ou dans un fichier texte joint. ou dans un fichier joint.
En tant que personnes sensibilisées à la sécurité des échanges électroniques, En tant que personnes sensibilisées à la sécurité des échanges
vous devriez m'envoyer vos rendus signés avec votre clef PGP. Pensez à électroniques, vous devrez m'envoyer vos rendus signés avec votre clef
[me](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x842807A84573CC96) faire PGP. Pensez à
signer votre clef et n'hésitez pas à [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/). [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} \hypersetup{linkcolor=black}
\tableofcontents \tableofcontents

View File

@ -1,6 +1,7 @@
\newpage \newpage
## Volumes Volumes
=======
Il est possible de partager des répertoires entre plusieurs conteneurs. Pour ce 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 faire, il faut déclarer dans le `Dockerfile` une ou plusieurs instructions
@ -38,7 +39,7 @@ Une lecture intéressante sur ce sujet est sans doute [cet article de Michael
Crosby](http://crosbymichael.com/advanced-docker-volumes.html). Crosby](http://crosbymichael.com/advanced-docker-volumes.html).
### Partage avec la machine hôte ## Partage avec la machine hôte
Il est possible de monter un répertoire de la machine hôte dans un 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 conteneur. L'intérêt reste plutôt limité à des fins de facilité ou de test, par
@ -54,7 +55,7 @@ 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 ! Downloads en renseignant l'IP de votre machine dans son navigateur favori !
### Data Volume Container ## Data Volume Container
Dans de nombreuses situations, il est intéressant de séparer les données de 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 l'application, et donc d'avoir un conteneur exécutant l'application et un
@ -111,4 +112,4 @@ vie !
terminal pour créer le *data volume container*, construire l'image à partir du terminal pour créer le *data volume container*, construire l'image à partir du
Dockerfile et lancer le conteneur `my_webserver` lié. Dockerfile et lancer le conteneur `my_webserver` lié.
Rendre le Dockerfile, son contexte et le script de construction/lancement. Rendre le `Dockerfile`, son contexte et le script de construction/lancement.

84
tutorial/1/what.md Normal file
View 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).

View File

@ -1,4 +1,4 @@
SOURCES = tutorial.md installation.md what.md first.md dockerfile.md volumes.md linking.md SOURCES = tutorial.md installation.md what.md first.md supervisor.md goodpractices.md compose.md project.md
TEMPLATE = ../../template.tex TEMPLATE = ../../template.tex
PANDOCOPTS = --latex-engine=xelatex \ PANDOCOPTS = --latex-engine=xelatex \
--standalone \ --standalone \

View File

@ -1,182 +1,85 @@
\newpage \newpage
# Mon premier conteneur # Premiers pas
Afin de tester la bonne marche de votre installation, exécutons la commande : Dans un premier temps, nous allons créer une image Docker comme si
l'on réalisait l'installation sur une machine classique : en suivant
``` une recette. La machine (notre première image Docker) contient tout le
docker run hello-world nécessaire pour faire fonctionner notre service.
```
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 ## Les caches
Maintenant que nous avons à notre disposition l'image d'un conteneur Ubuntu, Nous avons vu que chaque instruction de notre `Dockerfile` génère une
lançons-la ! couche. Chaque couche sert de cache d'une construction de conteneur à
l'autre. Ainsi, lorsque vous modifiez une instruction dans votre
`Dockerfile`, les instructions précédentes ne sont pas réexécutées
mais sont ressorties du cache.
La commande `run` de Docker prend comme derniers arguments le programme à Le cache se basant principalement sur le contenu de chaque instruction
lancer dans le conteneur ainsi que ses éventuels arguments. Essayons d'afficher dans le `Dockerfile` (pour les `COPY` et `ADD`, il va aussi regarder
un Hello World : la date de dernière modification de fichier copié ou ajouté). Donc
tant qu'une instruction n'est pas modifiée dans le `Dockerfile`, le
cache sera utilisé.
``` Il est possible de ne pas utiliser le cache et de relancer toutes les
docker run ubuntu /bin/echo "Hello World" étapes du `Dockerfile` en ajoutant l'option `--no-cache` au moment du
``` `docker build`.
Dans notre exemple, c'est bien le `/bin/echo` présent dans le conteneur qui est Les couches du cache peuvent être partagées entre plusieurs conteneur,
appelé. Ce n'est pas le programme `/bin/echo` de la machine hôte qui a été c'est ainsi que vous pouvez partager facilement une plus grosse partie
transféré dans le conteneur. du système de fichier (afin de profiter du cache du système de
fichiers au moment de l'exécution du conteneur).
## Modifier un conteneur ## `apt-get`
À chaque fois que nous lançons un `run`, un nouveau conteneur est créé à partir Pour profiter du cache, il faut donc placer les étapes les plus
de l'image que l'on a précisé (via un mécanisme de Copy-On-Write, c'est donc génériques (qui seraient susceptibles d'apparaître dans plusieurs
très rapide et ne consomme pas beaucoup d'espace disque). Cela signifie que conteneur), en haut du `Dockerfile`.
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 : Commençons donc notre `Dockerfile` : choisissez une image de base pour
votre `FROM`, et indiquez votre nom avec l'instruction `MAINTAINER`,
``` pour indiquez que c'est vous qui maintenez ce conteneur (si d'autres
docker run -it ubuntu /bin/bash gens ont besoin qu'il faut le mettre à jour par exemple).
```
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` !
## Métadonnées ## `RUN` ou script ?
Les images et les conteneurs sont composés d'un ensemble de couches de système ### InfluxDB
de fichiers et de métadonnées. Ces métadonnées stockent de nombreux paramètres
tels que la commande à lancer par défaut pour une image, ou le PID du premier
programme lancé pour un conteneur.
Vous pouvez affichez ces métadonnées avec la commande `docker inspect Ensuite viens l'installation d'InfluxDB. Le paquet n'est pas
hello-world`. disponible dans les dépôts. La
[https://influxdb.com/docs/v0.9/introduction/installation.html](procédure
décrite sur le site) incite à télécharger le paquet mis à disposition
puis à l'installer via `dpkg -i`.
Deux solutions s'offrent à nous : télécharger le paquet hors du
conteneur, le copier, puis l'installer. Ou faire un `RUN` avec toutes
ces opérations (sans oublier l'installation de `wget`/`curl`).
La copie étant définitive (supprimer le fichier ne le supprimera pas
des couches où il a pu exister), donc la seconde solution semble
préférable (mais `wget` restera en déchet).
Écrivez une commande `RUN` qui va télécharger la dernière version
d'InfluxDB, qui va l'installer et supprimer le fichier.
\vspace{1em}
À ce stade, nous pouvons déjà terminer le conteneur et tester
qu'InfluxDB est bien utilisable : `EXPOSE`, `CMD`, ... Il est possible
que vous ayez à écraser le fichier de configuration via un
`COPY`. Garder la ligne qui vous permet de lancer votre serveur web
dans un coin, en attendant la partie suivante.
## Rendu ### Grafana
### Questions Une fois InfluxDB configuré, nous allons avoir la même réflexion avec
Grafana.
1. Comment limiter la quantité maximale de mémoire qu'un conteneur pourra De la même manière, téléchargez, installez et supprimez le paquet.
utiliser ?
1. Un conteneur Docker se détache-t-il de tous les *namespaces* ? Si non, Lors de vos tests, sachez que vous pouvez vous connecter sur grafana avec
pourquoi ? l'utilisateur *admin*, mot de passe *admin*.
### Exercice
Réalisez un script permettant de rejoindre les namespaces d'un conteneur Docker
actif. Vous pouvez réutiliser votre exécutable `setns` du premier TP. Vous
allez sans doute avoir besoin d'utiliser `docker inspect`, n'hésitez pas à
consulter son aide : `docker inspect --help`.
```
host# ./docker-attach romantic_archimedes /bin/bash
inCntnr# _
```
`romantic_archimedes` correspond au nom du conteneur Docker auquel on veut
s'attacher et `/bin/bash`, la première commande que l'on va exécuter après le
`clone`.

View File

@ -2,118 +2,30 @@
# Installation # Installation
## Par le gestionnaire de paquets ## `docker-compose`
Sous Debian et ses dérivés (Ubuntu, Mint, ...) le paquet et la commande ont été L'équipe en charge de Docker compose met à disposition un binaire contenant
nommés `docker.io`. Vous pouvez vous créer un alias `alias docker=docker.io` si tous les scripts. Nous pouvons l'installer en suivant la procédure suivante :
celui-ci n'a pas déjà été défini.
Sous les autres distributions, `docker` correspond a priori bien à la solution
de virtualisation légère que l'on va utiliser.
### Debian Jessie
`docker` se trouve dans les backports, ajouter-les à votre `/etc/apt/sources.list` :
``` ```
deb http://ftp.debian.org/debian/ jessie-backports main non-free contrib curl -L https://github.com/docker/compose/releases/download/1.3.3/docker-compose-Linux-x86_64 > /usr/bin/docker-compose
chmod +x /usr/bin/docker-compose
``` ```
Puis : Le projet étant écrit en Python, il est également disponible via `pip`, si vous
préférez cette méthode. N'oubliez pas de préciser une version compatible avec
votre version de Docker.
### Vérification du fonctionnement
Comme avec Docker, nous pouvons vérifier le bon fonctionnement de
`docker-compose` en exécutant la commande :
``` ```
apt-get update 42sh$ docker-compose --version
apt-get install docker.io docker-compose version: 1.3.3
``` ```
### Debian Wheezy Si vous obtenez une réponse similaire, c'est que vous êtes prêt à commencer le
TP ! Alors n'attendons pas, partons à l'aventure !
Il vous faut utiliser le dépôt de paquets fourni par Docker :
```
apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
```
Ajoutez cette ligne dans votre `/etc/apt/sources.list` :
```
deb https://apt.dockerproject.org/repo debian-wheezy main
```
Puis :
```
apt-get update
apt-get install docker-engine
```
## 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
```
## 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.3.3
Client API version: 1.15
Go version (client): go1.3.3
Git commit (client): d344625
OS/Arch (client): linux/amd64
Server version: 1.3.3
Server API version: 1.15
Go version (server): go1.3.3
Git commit (server): d344625
```
### `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/articles/security/#docker-daemon-attack-surface>
## Rendu
### Questions
1. Dans quel langage Docker a-t-il été écrit ? Docker utilise la `libcontainer`
afin d'avoir une couche d'abstraction des *namespaces* et des
*cgroups*. Dois-je installer cette bibliothèque avant de recopier sur une
nouvelle machine le binaire `docker` (sans passer par le gestionnaire de
paquets) ? Pourquoi ?
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.

View File

@ -1,11 +1,11 @@
% Virtualisation légère -- TP n^o^ 2 % Virtualisation légère -- TP n^o^3
% Pierre-Olivier *Nemunaire* Mercier % Pierre-Olivier *Nemunaire* Mercier
% Jeudi 22 octobre 2015 % Jeudi 29 octobre 2015
Durant ce deuxième TP, nous allons apprendre à utiliser Docker ! Durant ce troisième TP, nous allons approfondir l'utilisation de Docker !
Tous les éléments de ce TP (exercices et questions) sont à rendre à Tous les éléments de ce TP (exercices et questions) sont à rendre à
<virli@nemunai.re> au plus tard le mercredi 28 octobre 2015 à 23 h 42. Consultez la <virli@nemunai.re> au plus tard le jeudi 12 novembre 2015 à 23 h 42. Consultez la
dernière section de chaque partie pour plus d'information sur les éléments à 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 rendre. Vous pouvez placer les réponses aux questions dans le corps du courriel
ou dans un fichier joint. ou dans un fichier joint.

View File

@ -1,68 +1,18 @@
\newpage \newpage
# Composition de Docker # But du TP
Docker est une suite d'outils de haut niveau, permettant d'utiliser très Aujourd'hui, nous allons terminer notre système de monitoring commencé lors du
facilement les conteneurs. premier TP.
Docker est composé d'un daemon lancé au démarrage de votre machine, avec lequel Le résultat attendu d'ici la fin du TP, est un groupe de conteneurs
vous interagissez via un client (le programme `docker`) qui se connecte au indépendants les uns des autres, réutilisables en fonction des besoins.
daemon au moyen d'une socket. Le client peut donc potentiellement être sur une
machine distincte du daemon où s'exécutent les conteneurs.
TODO image de graphana
## Les images Docker Nous reprendrons le script de monitoring que vous avez rendu au premier TP. Les
données collectées seront envoyés vers [https://influxdb.com/](InfluxDB), puis
Une image Docker est un système de fichiers en lecture seule. Il est formé d'un elles seront affichées sous forme de graphique dans
ensemble de couches, agrégées par un UnionFS. [http://grafana.org/](Grafana). L'interface sera servie par un reverse-proxy
qui vous permettra de n'ouvrir que le port 80 ou 443, pour accéder à
Par exemple, une image peut être un système Ubuntu complet ou juste busybox ou l'interface d'administration d'InfluxDB et à l'interface de Grafana.
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 (namespaces,
cgroups, capabilities, ...).
## 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) et 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).
## Rendu
1. À quoi correspondent les différents modes réseau utilisables dans Docker : à
quel type de réseau LXC (VLAN, MACVLAN-VEPA, veth, phys, ...)
correspondent-ils ? comment sont utilisés les *namespaces Network* ?
1. Quels sont les différents *storage drivers* de Docker ? décrivez-les en
quelques mots.

View File

@ -1,85 +0,0 @@
\newpage
# Premiers pas
Dans un premier temps, nous allons créer une image Docker comme si
l'on réalisait l'installation sur une machine classique : en suivant
une recette. La machine (notre première image Docker) contient tout le
nécessaire pour faire fonctionner notre service.
## Les caches
Nous avons vu que chaque instruction de notre `Dockerfile` génère une
couche. Chaque couche sert de cache d'une construction de conteneur à
l'autre. Ainsi, lorsque vous modifiez une instruction dans votre
`Dockerfile`, les instructions précédentes ne sont pas réexécutées
mais sont ressorties du cache.
Le cache se basant principalement sur le contenu de chaque instruction
dans le `Dockerfile` (pour les `COPY` et `ADD`, il va aussi regarder
la date de dernière modification de fichier copié ou ajouté). Donc
tant qu'une instruction n'est pas modifiée dans le `Dockerfile`, le
cache sera utilisé.
Il est possible de ne pas utiliser le cache et de relancer toutes les
étapes du `Dockerfile` en ajoutant l'option `--no-cache` au moment du
`docker build`.
Les couches du cache peuvent être partagées entre plusieurs conteneur,
c'est ainsi que vous pouvez partager facilement une plus grosse partie
du système de fichier (afin de profiter du cache du système de
fichiers au moment de l'exécution du conteneur).
## `apt-get`
Pour profiter du cache, il faut donc placer les étapes les plus
génériques (qui seraient susceptibles d'apparaître dans plusieurs
conteneur), en haut du `Dockerfile`.
Commençons donc notre `Dockerfile` : choisissez une image de base pour
votre `FROM`, et indiquez votre nom avec l'instruction `MAINTAINER`,
pour indiquez que c'est vous qui maintenez ce conteneur (si d'autres
gens ont besoin qu'il faut le mettre à jour par exemple).
## `RUN` ou script ?
### InfluxDB
Ensuite viens l'installation d'InfluxDB. Le paquet n'est pas
disponible dans les dépôts. La
[https://influxdb.com/docs/v0.9/introduction/installation.html](procédure
décrite sur le site) incite à télécharger le paquet mis à disposition
puis à l'installer via `dpkg -i`.
Deux solutions s'offrent à nous : télécharger le paquet hors du
conteneur, le copier, puis l'installer. Ou faire un `RUN` avec toutes
ces opérations (sans oublier l'installation de `wget`/`curl`).
La copie étant définitive (supprimer le fichier ne le supprimera pas
des couches où il a pu exister), donc la seconde solution semble
préférable (mais `wget` restera en déchet).
Écrivez une commande `RUN` qui va télécharger la dernière version
d'InfluxDB, qui va l'installer et supprimer le fichier.
\vspace{1em}
À ce stade, nous pouvons déjà terminer le conteneur et tester
qu'InfluxDB est bien utilisable : `EXPOSE`, `CMD`, ... Il est possible
que vous ayez à écraser le fichier de configuration via un
`COPY`. Garder la ligne qui vous permet de lancer votre serveur web
dans un coin, en attendant la partie suivante.
### Grafana
Une fois InfluxDB configuré, nous allons avoir la même réflexion avec
Grafana.
De la même manière, téléchargez, installez et supprimez le paquet.
Lors de vos tests, sachez que vous pouvez vous connecter sur grafana avec
l'utilisateur *admin*, mot de passe *admin*.

View File

@ -1,31 +0,0 @@
\newpage
# Installation
## `docker-compose`
L'équipe en charge de Docker compose met à disposition un binaire contenant
tous les scripts. Nous pouvons l'installer en suivant la procédure suivante :
```
curl -L https://github.com/docker/compose/releases/download/1.3.3/docker-compose-Linux-x86_64 > /usr/bin/docker-compose
chmod +x /usr/bin/docker-compose
```
Le projet étant écrit en Python, il est également disponible via `pip`, si vous
préférez cette méthode. N'oubliez pas de préciser une version compatible avec
votre version de Docker.
### Vérification du fonctionnement
Comme avec Docker, nous pouvons vérifier le bon fonctionnement de
`docker-compose` en exécutant la commande :
```
42sh$ docker-compose --version
docker-compose version: 1.3.3
```
Si vous obtenez une réponse similaire, c'est que vous êtes prêt à commencer le
TP ! Alors n'attendons pas, partons à l'aventure !

View File

@ -1,18 +0,0 @@
\newpage
# But du TP
Aujourd'hui, nous allons terminer notre système de monitoring commencé lors du
premier TP.
Le résultat attendu d'ici la fin du TP, est un groupe de conteneurs
indépendants les uns des autres, réutilisables en fonction des besoins.
TODO image de graphana
Nous reprendrons le script de monitoring que vous avez rendu au premier TP. Les
données collectées seront envoyés vers [https://influxdb.com/](InfluxDB), puis
elles seront affichées sous forme de graphique dans
[http://grafana.org/](Grafana). L'interface sera servie par un reverse-proxy
qui vous permettra de n'ouvrir que le port 80 ou 443, pour accéder à
l'interface d'administration d'InfluxDB et à l'interface de Grafana.

View File

@ -1,4 +1,4 @@
SOURCES = tutorial.md installation.md what.md first.md supervisor.md goodpractices.md compose.md project.md SOURCES = tutorial.md installation.md lxc.md cgroups.md namespaces.md
TEMPLATE = ../../template.tex TEMPLATE = ../../template.tex
PANDOCOPTS = --latex-engine=xelatex \ PANDOCOPTS = --latex-engine=xelatex \
--standalone \ --standalone \

View File

@ -0,0 +1,55 @@
\newpage
# Installation
## Noyau Linux
Ce TP requiert un noyau Linux 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
[*] 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
```
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`.
## LXC
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.
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.

View File

@ -1,14 +1,15 @@
% Virtualisation légère -- TP n^o^3 % Virtualisation légère -- TP n^o^ 1
% Pierre-Olivier *Nemunaire* Mercier % Pierre-Olivier *nemunaire* Mercier
% Jeudi 29 octobre 2015 % Jeudi 8 octobre 2015
Durant ce troisième TP, nous allons approfondir l'utilisation de Docker ! Le but de ce premier TP est d'utiliser les commandes et les appels systèmes vu
durant le cours.
Tous les éléments de ce TP (exercices et questions) sont à rendre à Tous les éléments de ce TP (exercices et questions) sont à rendre à
<virli@nemunai.re> au plus tard le jeudi 12 novembre 2015 à 23 h 42. Consultez la <virli@nemunai.re> au plus tard le **mercredi 21 octobre 2015 à 23
dernière section de chaque partie pour plus d'information sur les éléments à h 42**. Consultez la dernière section de chaque partie pour plus d'information
rendre. Vous pouvez placer les réponses aux questions dans le corps du courriel sur les éléments à rendre. Vous pouvez placer les réponses aux questions dans
ou dans un fichier joint. le corps du courriel ou dans un fichier texte joint.
En tant que personnes sensibilisées à la sécurité des échanges électroniques, 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 à vous devriez m'envoyer vos rendus signés avec votre clef PGP. Pensez à

1
tutorial/header.tex Normal file
View File

@ -0,0 +1 @@
\usepackage[cm]{fullpage}