Ready for j1
This commit is contained in:
parent
150f5a8834
commit
dc8544c0c9
@ -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
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`.
|
@ -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
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
|
\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.
|
||||||
|
@ -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
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.
|
@ -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
|
||||||
|
@ -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
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).
|
@ -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 \
|
||||||
|
@ -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# _
|
|
||||||
```
|
|
||||||
|
|
||||||
où `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`.
|
|
||||||
|
@ -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.
|
|
||||||
|
@ -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.
|
||||||
|
@ -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.
|
|
||||||
|
@ -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*.
|
|
@ -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 !
|
|
@ -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.
|
|
@ -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 \
|
55
tutorial/4/installation.md
Normal file
55
tutorial/4/installation.md
Normal 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.
|
@ -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
1
tutorial/header.tex
Normal file
@ -0,0 +1 @@
|
|||||||
|
\usepackage[cm]{fullpage}
|
Loading…
Reference in New Issue
Block a user