wiki/HowtoDocker.md

496 lines
12 KiB
Markdown
Raw Normal View History

2016-10-28 19:57:02 +02:00
---
title: Howto Docker
categories: docker
---
Docker est une solution qui permet de créer, déployer et gérer des conteneurs
Linux.
2016-10-28 19:57:02 +02:00
Documentation : <https://docs.docker.com/>
2018-01-18 18:02:58 +01:00
# Ecosystème
## Le démon docker et son client
Docker est une application en mode client-serveur. *dockerd* est un démon qui
fournit une API REST afin d'intéragir avec les conteneurs. *docker* est un
client qui permet d'interagir avec le démon en ligne de commande. Il peut
interagir avec un démon en local (sur la même machine) ou avec un démon sur une
machine distante.
## Les objets dockers
### Image
Une image est un template contenant des instructions pour créer un conteneur
docker. Ces instructions sont listées dans un fichier nommé *Dockerfile*. La
plupart du temps, une image se base sur une autre image ce qui crée un système
de couches. Lorsqu'on modifie une image, seules les couches qui sont modifiées
sont reconstruites.
Une fois qu'une image est créée on peut la publier dans un *registry* (`docker
push`).
### Conteneur
Un conteneur est une instance exécutable d'une image.
## docker registry
Un *registry* sert à héberger des images docker. Il existe des registres
publics tels que *docker hub* ou *docker cloud* mais il est possible d'héberger
son propre *registry*.
2016-10-28 19:57:02 +02:00
# Docker Engine
## Installation
Il est conseillé d'utiliser le paquet docker-ce des dépôts du projet Docker :
2017-01-20 10:51:37 +01:00
~~~
2017-09-11 20:07:36 +02:00
# apt install apt-transport-https
# echo "deb http://download.docker.com/linux/debian stretch stable" > /etc/apt/sources.list.d/docker.list
# curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add -
2016-10-28 19:57:02 +02:00
# apt update
2017-09-11 20:07:36 +02:00
# apt install docker-ce
2017-01-20 10:51:37 +01:00
~~~
2016-10-28 19:57:02 +02:00
2017-09-11 20:07:36 +02:00
> *Note* : Pour Debian 8 :
>
> ~~~
> # echo "deb http://apt.dockerproject.org/repo debian-jessie main" > /etc/apt/sources.list.d/docker.list
> # apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
> # apt update
> # apt install docker-engine
> ~~~
Sur sa machine de travail, il est utile d'ajouter son utilisateur dans le
groupe _docker_ pour pouvoir interagir avec le démon _dockerd_ sans passer root
à chaque fois :
~~~
# adduser $USER docker
~~~
2018-01-18 15:41:10 +01:00
### Configuration
#### Changer le chemin de stockage
Créer le fichier `/etc/docker/daemon.conf` et y mettre :
~~~
{
"graph": "<VOTRE_CHEMIN>",
"storage-driver": "overlay"
}
~~~
2016-10-28 19:57:02 +02:00
### Ansible
2017-09-11 20:07:36 +02:00
2016-10-28 19:57:02 +02:00
Le rôle docker-host permet d'installer le docker-engine sur un hôte distant et
de l'exposer à l'externe ou non.
La documentation concernant l'utilisation du rôle est située dans son répertoire.
#### TLS
2018-01-19 16:18:40 +01:00
2016-10-28 19:57:02 +02:00
Lorsque le docker-engine est exposé, il est important de le sécuriser avec TLS.
Au moment de l'installation, une version altérée de shellpki est copiée dans le
répertoire docker/tls. Ensuite, les certificats et la clé sont créés pour le
serveur. (`shellpki init`)
Pour autoriser des hôtes à se connecter à l'engine, il faut leur créer une clé
et un certificat.
Pour ce faire, il suffit de lancer le script:
2017-01-20 10:51:37 +01:00
~~~
2016-10-28 19:57:02 +02:00
/home/docker/tls$ ./shellpki create
2017-01-20 10:51:37 +01:00
~~~
2016-10-28 19:57:02 +02:00
Les fichiers seront créés, par défaut, dans le répertoire
`/home/docker/tls/files/$CN`
## Utilisation de base
Une image Docker contient un système minimal avec un ou plusieurs services.
2016-10-28 19:57:02 +02:00
Un conteneur quant à lui est une instance (créée à partir d'une image)
en cours d'exécution.
2016-10-28 19:57:02 +02:00
### Gérer les conteneurs
#### Lister les conteneurs
2018-01-19 16:18:40 +01:00
2017-01-20 10:51:37 +01:00
~~~
$ docker ps
2017-01-20 10:51:37 +01:00
~~~
2018-01-19 16:18:40 +01:00
Options utiles :
2018-01-19 16:18:40 +01:00
2017-01-20 10:51:37 +01:00
~~~
-a : lister tous les conteneurs
-l : lister les conteneurs récemment lancés
-q : lister uniquement les ID des conteneurs
2017-01-20 10:51:37 +01:00
~~~
2016-10-28 19:57:02 +02:00
#### Instancier un nouveau conteneur
2018-01-19 16:18:40 +01:00
2017-01-20 10:51:37 +01:00
~~~
$ docker run <image> [commande]
2017-01-20 10:51:37 +01:00
~~~
2018-01-19 16:18:40 +01:00
Options utiles :
2018-01-19 16:18:40 +01:00
2017-01-20 10:51:37 +01:00
~~~
--name="nom" : donner un nom au conteneur
-p port_hôte:port_conteneur : rendre un port accessible depuis l'hôte
-d : lancer le conteneur en mode 'détaché'
-it : lancer le conteneur en mode intéractif avec tty
2017-01-20 10:51:37 +01:00
~~~
2018-01-19 16:18:40 +01:00
#### Démarrer un conteneur existant
2016-10-28 19:57:02 +02:00
Un conteneur existant est un conteneur précédemment instancié avec `docker
run`.
2018-01-19 16:18:40 +01:00
2017-01-20 10:51:37 +01:00
~~~
$ docker start <ID ou nom du conteneur>
2017-01-20 10:51:37 +01:00
~~~
2018-01-19 16:18:40 +01:00
#### Éteindre ou tuer un conteneur
~~~
$ docker stop|kill <ID ou nom du conteneur>
~~~
2016-10-28 19:57:02 +02:00
Lorsque le conteneur n'est plus en fonction, il existe toujours et peut être
lister à l'aide de la commande `docker ps -a`
2016-10-28 19:57:02 +02:00
#### Supprimer un conteneur
2018-01-19 16:18:40 +01:00
2017-01-20 10:51:37 +01:00
~~~
$ docker rm <ID ou nom du conteneur>
2017-01-20 10:51:37 +01:00
~~~
2016-10-28 19:57:02 +02:00
#### Exécuter des commandes dans un conteneur en fonction
2018-01-19 16:18:40 +01:00
2017-01-20 10:51:37 +01:00
~~~
$ docker exec <ID ou nom du conteneur> <commande>
2017-01-20 10:51:37 +01:00
~~~
2018-01-19 16:18:40 +01:00
Options utiles :
2018-01-19 16:18:40 +01:00
2017-01-20 10:51:37 +01:00
~~~
-t : alloue un TTY
-i : attache stdin (mode interractif)
2017-01-20 10:51:37 +01:00
~~~
2016-10-28 19:57:02 +02:00
On utilise habituellement la commande suivante pour obtenir un shell dans un conteneur en fonction :
2018-01-19 16:18:40 +01:00
2017-01-20 10:51:37 +01:00
~~~
$ docker exec -ti <conteneur> bash
2017-01-20 10:51:37 +01:00
~~~
2018-01-19 16:18:40 +01:00
#### Visionner les journaux d'un conteneur
Il s'agit en fait de la sortie standard et la sortie d'erreur du processus
lancé à l'intérieur du conteneur :
2018-01-19 16:18:40 +01:00
2017-01-20 10:51:37 +01:00
~~~
$ docker logs <ID ou nom du conteneur>
2017-01-20 10:51:37 +01:00
~~~
2016-10-28 19:57:02 +02:00
Options utiles :
~~~
-f : suivre les logs en direct
-t : afficher un timestamp devant chaque ligne
~~~
2018-01-19 16:18:40 +01:00
#### Afficher les informations d'un conteneur
2018-01-19 16:18:40 +01:00
2017-01-20 10:51:37 +01:00
~~~
$ docker inspect <ID ou nom>
2017-01-20 10:51:37 +01:00
~~~
2016-10-28 19:57:02 +02:00
Cette commande s'applique généralement à n'importe quel objet Docker
(conteneur, image, service, réseau…) et donne une liste exhaustive des
attributs de l'objet, formaté en JSON.
2016-10-28 19:57:02 +02:00
### Gérer les images
2016-10-28 19:57:02 +02:00
#### Lister les images locales
2016-10-28 19:57:02 +02:00
~~~
$ docker image ls
~~~
#### Construire une image
Pour construire ou mettre à jour une image :
2018-01-19 16:18:40 +01:00
2017-01-20 10:51:37 +01:00
~~~
$ docker build <repertoire>
2017-01-20 10:51:37 +01:00
~~~
2016-10-28 19:57:02 +02:00
Le répertoire doit contenir un fichier _Dockerfile_ décrivant l'image à
construire.
Option utiles :
2018-01-19 16:18:40 +01:00
2017-01-20 10:51:37 +01:00
~~~
-t : ajoute un tag à l'image
2017-01-20 10:51:37 +01:00
~~~
2016-10-28 19:57:02 +02:00
#### Ajouter un tag à une image existante
2018-01-19 16:18:40 +01:00
2017-01-20 10:51:37 +01:00
~~~
$ docker tag <tag actuel> <nouveau tag>
~~~
#### Pousser une image sur un dépôt distant
~~~
$ docker push <image>
~~~
Avant de pousser une image, il est nécessaire de lui attribuer le bon _tag_ qui
doit contenir l'adresse du dépôt distant.
Par exemple pour pousser l'image _foo-image_ sur le dépôt Docker _registry.example.net:5000_ :
~~~
$ docker tag foo-image registry.example.net:5000/foo-image
$ docker push registry.example.net:5000/foo-image
~~~
#### Récupérer une image d'un dépôt distant
~~~
$ docker pull <image>
~~~
### Astuces
Éteindre/Tuer/Supprimer tous les conteneurs :
~~~
$ docker stop|kill|rm $(docker ps -aq)
~~~
Supprimer toutes les images :
~~~
$ docker rmi $(docker images -q)
2017-01-20 10:51:37 +01:00
~~~
2016-10-28 19:57:02 +02:00
2018-04-30 22:13:52 +02:00
Démarrer un conteneur existant avec un shell bash (pour des fins de debug par exemple) :
2018-01-19 16:18:40 +01:00
2017-01-20 10:51:37 +01:00
~~~
2016-10-28 19:57:02 +02:00
$ docker run -it IMAGE bash
2017-01-20 10:51:37 +01:00
~~~
2016-10-28 20:14:41 +02:00
### Dépannage
#### Problème de connectivité à l'intérieur des conteneurs
Solution:
Redémarrer le service docker
#### Espace insuffisant lors d'un build
Solutions:
- Vérifier que le "build context" n'est pas trop grand.
- Modifier la variable d'environnement DOCKER_TMPDIR .
- Créer un fichier .dockerignore pour exclure des fichiers et répertoires
du "build context"
*Build context: Tout ce qui se trouve à la racine du Dockerfile.*
2017-09-11 20:07:36 +02:00
## Dockerfile
Exemple :
~~~
FROM debian:stretch
MAINTAINER John Doe <jdoe@example.com>
ENV DEBIAN_FRONTEND noninteractive
RUN (apt-get update && apt-get upgrade -y -q && apt-get dist-upgrade -y -q && apt-get -y -q autoclean && apt-get -y -q autoremove)
RUN apt-get install -y -q mariadb-server
EXPOSE 3306
CMD ["mysqld"]
~~~
~~~
# ls
Dockerfile
# docker build -t mariadb .
# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mariadb latest 4bea99cda08c 8 minutes ago 470MB
debian stretch 5b712ae16dd7 3 days ago 100MB
~~~
## Utilisation avancée
### Swarm
Swarm permet de mettre en communication plusieurs hôtes Docker afin d'en former
un cluster. On pourra ainsi déployer des applications multi-conteneurs sur
plusieurs machines.
#### Initialiser le cluster
~~~
docker0# docker swarm init
~~~
Joindre les autres machines au cluster créé (il vous suffit généralement de copier-coller la commande retournée par `docker swarm init` :
~~~
docker1# docker swarm join --token <token> <IP du premier node>
~~~
Par défaut la machine sur laquelle le cluster a été initialisée a le rôle de
_manager_, et les suivantes ont le rôle de _worker_. On ne peut déployer de
nouveaux services que depuis les _managers_. Les _workers_ se contentent de
recevoir les services à rouler.
Pour ajouter des machines plus tard, il suffit de générer un nouveau token :
~~~
docker0# docker swarm join-token <manager|worker>
~~~
#### Lister les machines du cluster
~~~
# docker node ls
~~~
#### Ajouter des labels à une machine
~~~
# docker node update --label-add <clé>=<valeur> <machine>
~~~
Les _labels_ servent notamment à définir des contraintes de placement des
services lors de l'utilisation de _docker stack_.
### Compose/stack (docker stack)
Docker permet de déployer des infrastructures multi-conteneurs (_stacks_) simplement à l'aide de `docker stack` (anciennement Docker Compose, logiciel tier). Il est très utile dans le cadre de déploiement sur un cluster Swarm.
L'infra est à décrire dans un fichier YAML.
Déployer (ou mettre à jour à chaud) une nouvelle _stack_ :
~~~
# docker stack deploy -c <stack_name.yml> <stack name>
~~~
Lister les _stacks_ :
~~~
# docker stack ls
~~~
Lister les _services_, toutes _stacks_ confondues ou pour une _stack_ donnée :
~~~
# docker service ls
# docker stack services <stack name>
~~~
Lister les _tasks_ (replicas) d'une _stack_ ou d'un _service donnée :
~~~
# docker stack ps <stack name>
# docker service ps <service name>
~~~
Supprimer une _stack_ :
~~~
# docker stack rm <stack name>
~~~
### Réseaux (docker network)
Docker permet de gérer différentes topologies de réseaux pour connecter les conteneurs entre eux à l'aide de `docker network`.
_drivers_ réseau supportés :
- _bridge_ : utilise les bridges Linux ;
- _overlay_ : utilisé dans le cas d'un cluster Swarm, permet d'avoir un
réseau unique partagé entre tous les hôtes Docker et permet de faire du
load-balancing entre les conteneurs (replicas) d'un service ;
- _macvlan_ : permet d'assigner directement des adresses IP publiques aux
conteneurs, donc aucun NAT n'est fait contrairement aux précédents.
Créer un réseau :
~~~
# docker create -d <driver> […] <network name>
~~~
Lister les réseaux créés :
~~~
# docker network ls
~~~
Informations détaillées sur un réseau :
~~~
# docker network inspect <network name>
~~~
### Volumes (docker volume)
### Fichiers de configuration (docker config)
### Fichiers sensibles (docker secrets)
2018-05-02 16:51:24 +02:00
## FAQ
> Lors d'un redéploiement d'une stack Docker (docker stack deploy), les services ne sont pas redémarrer avec la nouvelle image
Vérifier que le tag _latest_ est bien précisé dans le nom de l'image dans le _docker-stack.yml_ :
~~~
image: registrydocker.example.com:5000/foo:latest
~~~
> Est-il possible de ne faire écouter un service d'une stack que sur une interface précise de la machine hôte (par exemple sur un LAN privé) ?
Non, Docker ne supporte pas ça.
Il faut bloquer le port en question dans le pare-feu, dans la chaîne iptables DOCKER-USER.
Pour bloquer l'accès au _registry_ Docker depuis l'extérieur par exemple :
~~~
# iptables -A DOCKER-USER -i eth0 -p tcp -m tcp --dport 5000 -j DROP
~~~
2018-05-02 18:15:30 +02:00
> Au sein des conteneurs, un `getent <service>` ou `getent tasks.<service>` ne retourne pas l'adresse IP du service alors que celui-ci est bien lancé.
Stopper le conteneur du service avec un `docker stop <conteneur du service>`. Docker stack devrait le relancer automatiquement.
> Comment obtenir l'adresse IP des _tasks_ d'un _service_ (dans le cadre de l'utilisation de Docker stack) au sein de conteneur (à des fins de debug) ?
L'adresse IP virtuelle qui redirige aléatoirement sur chacune des _tasks_ (si le réseau utilise le driver _overlay_, cas par défaut) :
~~~
$ getent hosts <nom du service>
~~~
L'adresse IP des différentes _tasks_ d'un service :
~~~
$ getent hosts tasks.<nom du service>
~~~