Split Docker tutorials into basis, orchestration and dockerfiles
This commit is contained in:
parent
2d364556a2
commit
2c48fa7942
36 changed files with 477 additions and 188 deletions
22
tutorial/docker-basis/Makefile
Normal file
22
tutorial/docker-basis/Makefile
Normal 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
|
62
tutorial/docker-basis/cleaning.md
Normal file
62
tutorial/docker-basis/cleaning.md
Normal 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 !
|
168
tutorial/docker-basis/first.md
Normal file
168
tutorial/docker-basis/first.md
Normal 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.
|
||||
|
||||
{ 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.
|
BIN
tutorial/docker-basis/img-vs-cntr.png
Normal file
BIN
tutorial/docker-basis/img-vs-cntr.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 247 KiB |
122
tutorial/docker-basis/installation.md
Normal file
122
tutorial/docker-basis/installation.md
Normal 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>
|
124
tutorial/docker-basis/linking.md
Normal file
124
tutorial/docker-basis/linking.md
Normal 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
|
||||
```
|
9
tutorial/docker-basis/misc/Dockerfile
Normal file
9
tutorial/docker-basis/misc/Dockerfile
Normal 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" ]
|
12
tutorial/docker-basis/misc/count_layers.sh
Executable file
12
tutorial/docker-basis/misc/count_layers.sh
Executable 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.
|
6
tutorial/docker-basis/misc/docker-entrypoint.sh
Executable file
6
tutorial/docker-basis/misc/docker-entrypoint.sh
Executable 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 $@
|
73
tutorial/docker-basis/misc/tumsoul_0.3.3.py
Executable file
73
tutorial/docker-basis/misc/tumsoul_0.3.3.py
Executable 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)
|
135
tutorial/docker-basis/rendu.md
Normal file
135
tutorial/docker-basis/rendu.md
Normal 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.
|
2
tutorial/docker-basis/todo.org
Normal file
2
tutorial/docker-basis/todo.org
Normal file
|
@ -0,0 +1,2 @@
|
|||
* entrypoint
|
||||
* parler du contexte du build
|
23
tutorial/docker-basis/tutorial.md
Normal file
23
tutorial/docker-basis/tutorial.md
Normal 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
|
85
tutorial/docker-basis/volumes.md
Normal file
85
tutorial/docker-basis/volumes.md
Normal 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*.
|
74
tutorial/docker-basis/what.md
Normal file
74
tutorial/docker-basis/what.md
Normal 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.
|
Loading…
Add table
Add a link
Reference in a new issue