Split Docker tutorials into basis, orchestration and dockerfiles

This commit is contained in:
nemunaire 2017-10-15 22:49:27 +02:00
parent 2d364556a2
commit 2c48fa7942
36 changed files with 477 additions and 188 deletions

View file

@ -0,0 +1,22 @@
SOURCES = tutorial.md installation.md what.md first.md cleaning.md volumes.md linking.md rendu.md
PANDOCOPTS = --latex-engine=xelatex \
--standalone \
--normalize \
--number-sections \
--smart \
-M lang=french \
-M fontsize=12pt \
-M papersize=a4paper \
-M mainfont="Linux Libertine O" \
-M monofont="FantasqueSansMono-Regular" \
-M sansfont="Linux Biolinum O" \
--include-in-header=../header.tex
all: tutorial.pdf
tutorial.pdf: ${SOURCES}
pandoc ${PANDOCOPTS} -o $@ $+
clean::
rm tutorial.pdf

View file

@ -0,0 +1,62 @@
\newpage
Faire le ménage
===============
Au fur et à mesure de nos 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 essai 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ée à un conteneur ; il ne
supprimera pas non plus les conteneurs qui n'auront pas été démarrés avec
l'option `--rm`.
## Conteneurs
Vous pouvez afficher l'ensemble des conteneurs, quelque soit leur état (en
cours d'exécution, arrêtés, ...) avec la commande suivante :
```
42sh$ docker container ls -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 container rm 0e8bbff6d500 552d71619723
```
ou encore :
```
docker container 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
gérer de la même manière que les conteneurs, avec les sous-commandes `docker
image`.
## `prune`
Dans la plupart des menus permettant de gérer les objets Docker, vous trouverez
une commande `prune` qui supprimera les objets inutilisés.
## `docker-gc`
Vous pouvez également utiliser l'image <https://github.com/spotify/docker-gc>
pour effectuer le ménage de manière automatique, plus fréquemment. Mais
attention à vos données !

View file

@ -0,0 +1,168 @@
\newpage
Mon premier conteneur
=====================
Afin de tester la bonne marche de notre installation, lançons notre premier
conteneur avec la commande :
```
docker container 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
`store.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
Store, 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 Store en utilisant la commande `pull` :
```
docker image pull ubuntu
```
Remarquez comment on interagit avec chaque *objet Docker* : dans la ligne de
commande, le premier mot clef est le type d'objet (`container`, `image`,
`service`, `network`, `volume`, ...) ; ensuite, vient l'action que l'on
souhaite faire dans ce cadre.[^oldcall]
[^oldcall]: cela n'a pas toujours été aussi simple, cette syntaxe n'existe que
depuis la version 1.13 (janvier 2017). C'est pourquoi, lorsque vous ferez
des recherches sur internet, vous trouverez de nombreux articles utilisant
l'ancienne syntaxe, sans le type d'objets : `docker images` au lieu de
`docker image ls`, `docker run` au lieu de `docker container run`, ...
L'ancienne syntaxe est dépréciée, mais il reste actuellement possible de
l'utiliser.
Par exemple, 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 `ls` sous le type d'objets `image` :
```
docker image ls
```
Vous devriez constater la présence de plusieurs images « Ubuntu », mais chacune
a un *TAG* différent. En effet, souven, il existe plusieurs versions d'une même
image. Pour Ubuntu par exemple, nous avons la possibilité de lancer la version
`trusty`, `xenial`, `zesty` ou `artful`.
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é par défaut : *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 container 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.
Pour nous en convaincre, nous pouvons tenter d'exécuter un programme qui n'est
pas présent sur notre machine, mais bien uniquement dans le conteneur. Si vous
n'utilisez pas [Alpine Linux](https://www.alpine-linux.org), vous pourriez
tenter d'utiliser son gestionnaire de paquet `apk`, via :
```
docker container run alpine /sbin/apk stats
```
## Modifier une image
### Images vs. conteneurs
À chaque fois que nous lançons un `run`, un nouveau conteneur est créé.
L'image fournie comme argument est utilisée comme un modèle de base pour le
conteneur et est recopiée grâce à un mécanisme de *Copy-On-Write*: c'est donc
très rapide et ne consomme pas beaucoup d'espace disque.
Étant donné que chaque conteneur est créé à partir d'un modèle, 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.
![Images vs. conteneurs](img-vs-cntr.png "Images vs. conteneurs"){ width=85% }
Dans ce schéma, on considère les images comme étant la partie figée de Docker à
partir desquelles on peut créer des conteneurs.
Si l'on souhaite qu'une modification faite dans un conteneur (par exemple
l'installation d'un paquet) s'applique à d'autres conteneurs, il va falloir
créer une nouvelle image à partir de ce conteneur.
### Les paramètres
Pour créer une image, commençons par entrer dans un nouveau conteneur :
```
docker container run -it ubuntu /bin/bash
```
Vous avez remarqué l'utilisation des options `--tty` et `--interactive` ? Avant
le nom de l'image, elles sont gérées par Docker pour modifier le comportement
du `run`. En fait, tout comme `git(1)` et ses sous-commandes, chaque niveau de
commande peut prendre des paramètres :
```
docker DOCKER_PARAMS container run RUN_OPTS image IMAGE_CMD IMAGE_ARGS ...
```
Par exemple :
```
docker -H unix:///var/run/docker.sock container run -it alpine /bin/ash -c "echo foo"
```
Ici, l'option `-H` sera traitée par le client Docker (pour définir
l'emplacement du point de communication avec le daemon), tandis que les options
`-it` seront utilisées lors du traitement du `run`. Quant au `-c`, il sera
simplement tranmis au conteneur comme argument au premier `execve(2)` du
conteneur.
Avec l'option `--interactive`, on s'assure que l'entrée standard ne sera pas
fermée (`close(2)`). Nous demandons également l'allocation d'un TTY,
sans quoi `bash` ne se lancera pas en mode interractif[^bashnointer].
[^bashnointer]: Mais il sera possible de l'utiliser sans allouer de TTY, comme
par exemple en faisant :
```
42sh$ cat cmd
echo foo
42sh$ cat cmd | docker run -i busybox
foo
```
L'option `-i` reste néanmoins nécessaire pour que l'entrée standard soit
transmise au conteneur.

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 KiB

View file

@ -0,0 +1,122 @@
\newpage
Installation
============
## Prérequis
Docker repose sur plusieurs techniques implémentés dans les récents noyaux
Linux. Nous consacrerons les prochains cours à comprendre leurs
fonctionnement. Ces techniques ne sont pas limitées à une architecture de
microprocesseur spécifique (comme peuvent l'être les instructions de
virtualisation nécessaire pour rendre les hyperviseurs attractifs) ; cependant
la communauté autour de Docker utilisant principalement l'architecture `amd64`,
c'est sur cette dernière que Docker pourra être exploité à son plein potentiel.
Avant de continuer, assurez-vous que votre machine a bien démarré sur un noyau
64 bits. Le retour de la commande `uname -m` doit vous indiquer :
```
x86_64
```
Assurez-vous également d'avoir un noyau récent, avec la commande `uname -r` :
```
4.13.4-gentoo
```
Vous ne pourrez pas utiliser Docker avec un noyau antérieur à la version 3.10.
## Par le gestionnaire de paquets
En général, votre distribution mettra à votre disposition une version de Docker
plus ou moins récente. Sous Debian et ses dérivés (Ubuntu, Mint, ...) le paquet
a été nommé `docker.io`.
Si dans un environnement de production, on préférera sans doute utiliser une
version déjà bien éprouvée, pour ce cours, nous allons avoir besoin de la
dernière version disponible. Référez-vous à la documentation officielle
correspondant à votre distribution :
<https://docs.docker.com/engine/installation/linux/docker-ce/debian/>
### Versions de Docker
Historiquement, Docker est un projet open-source. Depuis peu, le business-model
de la société a évolué et ils proposent désormais deux éditions : *Community
Edition* et *Enterprise Edition*. La seconde est payante et possède un certain
nombre d'atouts pour faciliter son adoption en entreprise (notamment pas mal
d'interface graphique, etc.). Le cœur de la technologie est quant à lui
entièrement présent dans l'édition communautaire.
Depuis mars dernier, les numéros de version de Docker sont tirés de l'année et
du mois de parution (comme on a l'habitude avec Ubuntu 16.04 par exemple). Le
rythme actuel de parution est d'une version par trimestre (mars, juin,
septembre, décembre).[^versions]
[^versions]: Tous les détails sur les versions (CE/EE et numérotation,
fréquences, ...) sont résumés dans cette annonce :
<https://blog.docker.com/2017/03/docker-enterprise-edition/>
## Évaluation en ligne
Si vous rencontrez des difficultés pour vous lancer, le projet
[Play With Docker](https://play-with-docker.com/) vous donne accès à
un bac à sable dans lequel vous pourrez commencer à faire ce TP.
## 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: 17.09.0-ce
API version: 1.32
Go version: go1.9
Git commit: cec0b72
Built: Thu Sep 14 19:57:50 2017
OS/Arch: linux/amd64
Server:
Version: 17.09.0-ce
API version: 1.32 (minimum version 1.12)
Go version: go1.9
Git commit: cec0b72
Built: Thu Sep 14 21:50:58 2017
OS/Arch: linux/amd64
Experimental: false
```
### `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>

View file

@ -0,0 +1,124 @@
\newpage
Lier les conteneurs
===================
Partager des fichiers est une chose, mais ce n'est pas une manière d'échanger
de l'information de manière instantanée : les sockets et le réseau restent
plus adaptés à ce genre d'échanges.
## Les pilotes réseau
Docker propose de base trois *drivers* pour « gérer » cela :
- `none` : pour limiter les interfaces réseaux du conteneur à l'interface de
loopback `lo` ;
- `host` : pour partager la pile réseau avec l'hôte ;
- `bridge` : pour créer une nouvelle pile réseau par conteneur et rejoindre un
pont réseau dédié.
Ces trois *drivers* sont instanciés de base dans Docker avec le même nom que
leur pilote. Pour consulter la liste de réseaux utilisables, utilisez :
```
42sh$ docker network ls
NETWORK ID NAME DRIVER SCOPE
74cedd3ff385 bridge bridge local
d5d907add6e2 host host local
16b702ed01a0 none null local
```
Par défaut, c'est le réseau `bridge` (utilisant le pilote `bridge`) qui est
utilisé : ce réseau utilise le pont `docker0` que vous pouvez voir dans vos
interfaces réseaux via `ip link`. C'est via ce pont que les conteneurs peuvent
avoir accès à Internet, au travers une couche de NAT.
Pour changer le réseau principal joint par le conteneur, utiliser l'option
`--network` du `docker container run`.
### Le réseau `host`
En utilisant ce réseau, vous abandonnez tout isolation sur cette partie du
conteneur et partagez donc avec l'hôte toute sa pile IP. Cela signifie, entre
autre, que les ports que vous chercherez à allouer dans le conteneur devront
être disponibles dans la pile de l'hôte et également que tout port allouer sera
directement accessible, sans avoir à utiliser l'option `-p` du `run`.
### Créer un nouveau réseau `bridge`
Afin de contrôler quels échanges peuvent être réaliser entre les conteneurs, il
est recommandé de créer des réseaux utilisateur.
La création d'un réseau se fait tout simplement au travers des sous-commandes
relatives aux objets Docker `network` :
```
docker network create --driver bridge my_network
```
C'est ensuite ce nom de réseau que vous passerez à l'option `--network` de vos
`run`, ou vous pouvez également faire rejoindre un conteneur déjà lancé à un
réseau :
```
docker network connect NETWORK CONTAINER
```
Lorsque plusieurs conteneurs ont rejoint un réseau utilisateur, ils peuvent
mutuellement se découvrir grâce à un système de résolution de nom basé sur leur
nom de conteneur.
### Exercice
Lancez votre serveur web avec :
```
docker container run --name helloapp -d my_webserver
```
Puis créez un réseau utilisateur, rejoignez-le et lancez un conteneur dans le
même réseau utilisateur. Vous devriez être capable de lancer dans ce conteneur
les commandes :
```
ping helloapp
curl http://helloapp/
```
## Liaison à l'ancienne
Les réseaux utilisateurs sont la manière à privilégier lorsque l'on souhaite
que deux conteneurs puissent discuter entre-eux.
Avant que cela n'existe, une fonctionnalité de liaison existait. Moins
puissante que les réseaux et plus contraignante car elle nécessite de gérer
l'ordre dans lequel les conteneurs sont lancés, elle permet néanmoins de
partager en plus les variables d'environnements du conteneur lié.
Par exemple, pour lancer un conteneur `postgresql`, il faut lui préciser au
moins le mot de passe à utiliser via la variable d'environnement
`POSTGRES_PASSWORD`[^storepostgres] :
[^storepostgres]: l'ensemble des variables que peut prendre le conteneur est
disponible sur la page dédié à l'image sur le store :
<https://store.docker.com/images/postgres>
```
docker container run --name some-postgres -e POSTGRES_PASSWORD=mysecretpassword -d postgres
```
Le lien permet de fournir à n'importe quel autre conteneur les mêmes variables
d'environnement. Cela évite d'avoir à recopier le mot de passe pour lancer un
conteneur qui en dépendrait. Comme par exemple dans le cas d'un serveur web qui
doit se connecter à une base de données : l'application doit être configurée
pour utiliser le mot de passe défini au lancement du conteneur de base de
données :
```
docker run -it --rm --link some-postgres:postgres postgres psql -h postgres -U postgres
```

View file

@ -0,0 +1,9 @@
FROM alpine
RUN apk add --no-cache python
COPY tumsoul_0.3.3.py /tumsoul_0.3.3.py
COPY docker-entrypoint.sh /docker-entrypoint.sh
ENTRYPOINT [ "/docker-entrypoint.sh" ]
CMD [ "/tumsoul_0.3.3.py" ]

View file

@ -0,0 +1,12 @@
#!/bin/sh
IMG="$1"
NLAYER=0
while [ "$IMG" != "" ] && [ "$IMG" != "null" ]
do
IMG=$(docker inspect "$IMG" | jq .[0].Parent | tr -d '"')
NLAYER=$(($NLAYER + 1))
done
echo $1 image has $NLAYER layers.

View file

@ -0,0 +1,6 @@
#!/bin/sh
[ -n "${NETSOUL_LOGIN}" ] && sed -i "s/user = '%LOGIN%'/user = '${NETSOUL_LOGIN}'/" /tumsoul_0.3.3.py
[ -n "${PASSSOCKS}" ] && sed -i "s/password = '%PASSSOCKS%'/password = '${PASSSOCKS}'/" /tumsoul_0.3.3.py
exec $@

View file

@ -0,0 +1,73 @@
#!/usr/bin/python2
import socket,md5,string,time,os,sys,random,urllib
##### CONFIG :-) ##########
tumsoul_server = ('ns-server.epitech.net', 4242)
testlink_server = ('10.224.3.42', 80)
testlink_server2 = ('10.224.2.42', 80)
user = '%LOGIN%'
password = '%PASSSOCKS%'
location = urllib.quote("Tumsoul 0.3.3")
useragent = urllib.quote("tumsoul v0.3 [%d]"%os.getpid())
states = ["actif", "away"]
delays = [ 10 ] + [ 4242 ]*10 + [ 424 ]*10
def md5sum(str):
chk = md5.new()
chk.update(str)
return chk.digest()
def hexify(str):
return string.join(map(lambda c:"%02x"%ord(c),str),"")
def expect(file,str):
reply = file.readline()[:-1]
if reply[:len(str)]==str: return
print "Invalid reply : '%s*' expected, '%s' received."%(str,reply)
time.sleep(3)
sys.exit()
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(tumsoul_server)
f=s.makefile()
cmd,code,hash,ip,port,timestamp=string.split(f.readline()[:-1])
sum=md5sum("%s-%s/%s%s"%(hash,ip,port,password))
authreply=hexify(sum)
s.send("auth_ag ext_user none none\n")
expect(f,"rep 002 --")
s.send("ext_user_log %s %s %s %s\n"%(user,authreply,location,useragent))
expect(f,"rep 002 --")
print "Authentication complete, proceeding..."
statechange_deadline = string.atoi(timestamp)
ping_deadline = string.atoi(timestamp)
ping_delay = 40
check_delay = 10
check_deadline = string.atoi(timestamp)
localtime = string.atoi(timestamp)
while 1:
localtime += 1
if localtime > ping_deadline:
ping_deadline += ping_delay
#print "Ping..."
s.send("ping\n")
if localtime > statechange_deadline:
delay = random.choice(delays)
newstate = random.choice(states)
statechange_deadline+=delay
#print "Newstate : %s, next change in %d seconds..."%(newstate,delay)
s.send("state %s:%i\n"%(newstate,int(localtime)))
if localtime > check_deadline:
scheck = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
try: scheck.connect(testlink_server)
except:
print "Could not connect to %s:%d, trying next server in 5 seconds..."%testlink_server
time.sleep(5)
try: scheck.connect(testlink_server2)
except:
print "Could not connect to %s:%d, exitting in 5 seconds..."%testlink_server2
time.sleep(5)
sys.exit()
print "Connection to %s:%d OK."%testlink_server
scheck.close()
check_deadline+=check_delay
time.sleep(1)

View file

@ -0,0 +1,135 @@
\newpage
Projet et rendu
===============
## Projet
Écrivez un script `mycloud-run.sh` pour automatiser le lancement de
votre instance personnelle d'`owncloud`. Une attention particulière
devra être apportée à la manière dont vous gérerez le rappel du script
pour éventuellement relancer un conteneur qui se serait arrêté
(évidemment sans perdre les données).
À la fin de son exécution, le script affichera un lien utilisable sur l'hôte
pour se rendre sur la page de connexion. Une autre machine de votre réseau
local devrait également pouvoir accéder à la plate-forme, simplement en
renseignant l'IP de votre machine et en ajoutant éventuellement des règles de
pare-feu (mais cette dernière partie n'est pas demandée, gardez simplement en
tête que cela doit pouvoir être fait manuellement au cas par cas : sur une
machine sans pare-feu configurée, cela ne demande pas d'étape supplémentaire).
Votre script devra se limiter aux notions vues durant ce TP (ie. sans utiliser
`docker-compose` ou `docker stack` que l'on verra au prochain TP). Il pourra
cependant faire usage des commandes `docker OBJECT inspect` pour ne pas avoir à
parser les retours des commandes lisibles par les humains.
Cette instance devra utiliser une base de données MySQL (lancée par vos soins
dans un autre conteneur) et contenir ses données dans un ou plusieurs volumes
(afin qu'elles persistent à une mise à jour des conteneurs par exemple).
L'exécution doit être la plus sécurisée possible (pas de port MySQL exposé sur
l'hôte par exemple, etc.) et la plus respectueuse des bonnes pratiques que l'on
a pu voir durant ce premier cours.
## Modalité de rendu
Un service automatique s'occupe de réceptionner vos rendus, de faire des
vérifications élémentaires 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é et/ou reçu après la correction ne
sera pas pris en compte.
Par ailleurs, n'oubliez pas de répondre à
[l'évaluation du cours](https://www.epitaf.fr/moodle/mod/quiz/view.php?id=20).
## Tarball
Tous les fichiers identifiés comme étant à rendre pour ce TP sont à
placer dans une tarball (pas d'archive ZIP, RAR, ...).
Voici une arborescence type:
```
login_x-TP1/webserver
login_x-TP1/webserver/Dockerfile
login_x-TP1/webserver/index.html
login_x-TP1/mycloud
login_x-TP1/mycloud/mycloud-run.sh
```
## 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 au service 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.
#### I've decided to skip your e-mail
Si vous recevez un rapport concluant ainsi :
```
After analyzing your e-mail, I've decided to SKIP it.
```
Cela signifie que la lecture de votre courriel qui a été de préférée n'est pas
celle d'un rendu. Vérifiez que vous n'envoyez pas votre clef publique avec
votre rendu.

View file

@ -0,0 +1,2 @@
* entrypoint
* parler du contexte du build

View file

@ -0,0 +1,23 @@
---
title: Virtualisation légère -- TP n^o^ 1
subtitle: Les bases de Docker
author: Pierre-Olivier *Nemunaire* Mercier
institute: EPITA
date: Jeudi 5 octobre 2017
...
Durant ce premier TP, nous allons apprendre à utiliser Docker !
Tous les éléments de ce TP (exercices et projet) sont à rendre à
<virli@nemunai.re> au plus tard le jeudi 19 octobre 2017 à 8 h 42. Consultez la
dernière section de chaque partie pour plus d'information sur les éléments à
rendre.
En tant que personnes sensibilisées à la sécurité des échanges électroniques,
vous devrez m'envoyer vos rendus signés avec votre clef PGP. Pensez à
[me](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x842807A84573CC96) faire
signer votre clef et n'hésitez pas à
[faire signer la votre](http://www.meetup.com/fr/Paris-certification-de-cles-PGP-et-CAcert/).
\hypersetup{linkcolor=black}
\tableofcontents

View file

@ -0,0 +1,85 @@
\newpage
Stockage de données applicatives
================================
Le concept principal de Docker est de concevoir des conteneurs applicatifs : on
va préférer assigner un unique rôle à un conteneur (donc géralement on ne va
lancer qu'une seule application par conteneur) et concevoir un service complet
en créant un groupe de conteneur, partageant des données entre-eux par des
volumes.
Il est possible d'utiliser la dernière couche en lecture/écriture pour inscrire
des données. Il n'est cependant pas recommandé de stocker des données de cette
manière, car les données ne vont pas persister une fois que le conteneur aura
terminé son exécution ; elles seront alors plus compliqués à retrouver
manuellement.
Docker met à notre disposition plusieurs mécanismes pour que les données de nos
applications persistent et soient prêtes à migrer plus facilement vers une
solution plus apte à la décentralisation.
## Partage avec la machine hôte
Il est possible de monter un répertoire de la machine hôte dans un
conteneur. L'intérêt reste plutôt limité à des fins de facilité ou de test, par
exemple si vous voulez partager des fichiers avec votre voisin, en passant par
le protocole HTTP, mais sans se casser la tête à installer et configurer un
serveur web :
```
docker container run --rm -p 80:80 -v ~/Downloads:/usr/share/nginx/html:ro -d nginx
```
Une fois cette commande lancée, votre voisin pourra accéder à votre dossier
Downloads en renseignant l'IP de votre machine dans son navigateur favori !
## Les volumes
Les volumes sont des espaces créés via Docker (il s'agit d'objets Docker). Ils
permettent de partager facilement des données entre conteneurs, sans avoir à se
soucier de leur réel emplacement.
Comme il s'agit d'un objet, la première chose à faire va être de créer notre
volume :
```
docker volume create prod_db
```
Ensuite, nous pouvons démarrer un conteneur l'utilisant, par exemple :
```
docker container run --name mydb --mount source=prod_db,target=/var/lib/mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw mysql
```
Lorsque le volume est vide, si des données sont présentes à l'endroit du point
de montage, celles-ci sont recopiées dans le volume.
Si plus tard, vous souhaitez créer un conteneur chargé de faire des
sauvegardes, vous pourriez le lancer comme ceci :
```
docker container run -it --volume-from mydb busybox /bin/bash
```
## Volumes temporaires
Lorsque vous n'avez pas besoin de stocker les données et que vous ne désirez
pas qu'elles persistent (des données sensibles par exemple) ou si cela peut
améliorer les performances de votre conteneur, il est possible de créer des
points de montages utilisant le système de fichiers `tmpfs` et donc résidant
exclusivement en RAM.
## Rendu
### Exercice
Modifiez le `Dockerfile` de l'exercice précédent pour que les logs de votre
application web (ok, c'est juste un `index.html` ...) soient contenus dans un
*volume*.

View file

@ -0,0 +1,74 @@
\newpage
Composition de Docker
=====================
Docker est un écosystème d'outils de haut niveau, permettant d'utiliser des
*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 sur une API REST au travers d'une
socket. D'ailleurs, le client peut ne pas être sur la même machine qui
exécutera effectivement les conteneurs.
C'est ce qu'il se passe lorsqu'on utilise *Docker4Windows* ou *Docker4Mac* :
une machine virtuelle Linux est lancé parallèlement au système de base et
chaque commande `docker` tappée est passée au deamon dans la machine virtuelle.[^dockermachine]
[^dockermachine]: Il suffit de modifier la variable d'environnement
`DOCKER_HOST` ou de passer le paramètre `-H` suivi de l'URL de la socket à
`docker`. Voir aussi : <https://docs.docker.com/machine/overview/>
## `runc` et `containerd`
La notion de conteneurs est maintenant normalisée 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ée au programme
`containerd`, également issu de l'initiative. Lui aussi est 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. Elle est formée
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 sont situé en dehors,
ni accéder aux fichiers extérieurs.
## 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 envoyer.
Le registre utilisé de base est le [Docker Store](https://store.docker.com/) :
il contient à la fois des images officielles (ubuntu, debian, nginx, ...) et
des images crées par des utilisateurs.