« Docker » : différence entre les versions

De Justine's wiki
Aller à la navigation Aller à la recherche
Aucun résumé des modifications
Aucun résumé des modifications
Ligne 256 : Ligne 256 :
Dans une application distribuée, les différentes parties de l'application sont appellées "services". Par exemple, si l'on prend un site web de streaming vidéo, l'un des services servirait à à stocker les données dans une BDD; un autre à encoder les vidéos; etc.
Dans une application distribuée, les différentes parties de l'application sont appellées "services". Par exemple, si l'on prend un site web de streaming vidéo, l'un des services servirait à à stocker les données dans une BDD; un autre à encoder les vidéos; etc.


Les services ne sont finalement que des conteneurs en prod. Un service fait tourner une image, mais il codify la façon dont elle tourne : quels ports utiliser, combien de répliques du conteneur, etc. Le scaling d'un service change le nombre d'instances d'un conteneur seront lancées pour un même logiciel, afin d'assigner plus de ressources matérielles au processus. Les services peuvent être définis, lancés et scalés grâce au fichier docker-compose.yaml.
Les services ne sont finalement que des conteneurs en prod. Un service fait tourner une image, mais il codify la façon dont elle tourne : quels ports utiliser, combien de répliques du conteneur, etc. Le scaling d'un service change le nombre d'instances d'un conteneur seront lancées pour un même logiciel, afin d'assigner plus de ressources matérielles au processus. Les services peuvent être définis, lancés et scalés grâce au fichier docker-compose.yaml.


== Notre premier docker-compose.yml ==
== Notre premier docker-compose.yml ==
Ligne 262 : Ligne 262 :
Ce fichier définit la façon dont les conteneurs doivent se comporter en production.
Ce fichier définit la façon dont les conteneurs doivent se comporter en production.


Une foit que l'on s'est assuré que notre image a bien été push, on peut créer le fichier n'importe où et y coller le contenu suivant :
Une foit que l'on s'est assuré que notre image a bien été push, on peut créer le fichier n'importe où et y coller le contenu suivant :
<div style="background:#eeeeee; border:1px solid #cccccc; padding:5px 10px"><div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">version</span><span class="pi">:</span> <span class="s2">"</span><span class="s">3"</span>
<div style="background:#eeeeee; border:1px solid #cccccc; padding:5px 10px"><div class="language-yaml highlighter-rouge"><div class="highlight">
<span class="na">services</span><span class="pi">:</span>
version: "3"<br/> services:<br/> &nbsp; web:<br/> &nbsp;&nbsp;&nbsp; # replace username/repo:tag with your name and image details<br/> &nbsp;&nbsp;&nbsp; image: username/repo:tag<br/> &nbsp;&nbsp;&nbsp; deploy:<br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; replicas: 5<br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; resources:<br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; limits:<br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cpus: "0.1"<br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; memory: 50M<br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; restart_policy:<br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; condition: on-failure<br/> &nbsp;&nbsp;&nbsp; ports:<br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - "4000:80"<br/> &nbsp;&nbsp;&nbsp; networks:<br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - webnet<br/> networks:<br/> &nbsp; webnet:
  <span class="na">web</span><span class="pi">:</span>
 
    <span class="c1"># replace username/repo:tag with your name and image details</span>
&nbsp;
    <span class="na">image</span><span class="pi">:</span> <span class="s">username/repo:tag</span>
    <span class="na">deploy</span><span class="pi">:</span>
      <span class="na">replicas</span><span class="pi">:</span> <span class="s">5</span>
      <span class="na">resources</span><span class="pi">:</span>
        <span class="na">limits</span><span class="pi">:</span>
          <span class="na">cpus</span><span class="pi">:</span> <span class="s2">"</span><span class="s">0.1"</span>
          <span class="na">memory</span><span class="pi">:</span> <span class="s">50M</span>
      <span class="na">restart_policy</span><span class="pi">:</span>
        <span class="na">condition</span><span class="pi">:</span> <span class="s">on-failure</span>
    <span class="na">ports</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s2">"</span><span class="s">4000:80"</span>
    <span class="na">networks</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">webnet</span>
<span class="na">networks</span><span class="pi">:</span>
  <span class="na">webnet</span><span class="pi">:</span>
</code></pre>
</div> </div> </div>  
</div> </div> </div>  
Ce fichier accomplit les choses suivantes :
Ce fichier accomplit les choses suivantes&nbsp;:


*Faire un pull de l'image  
*Faire un pull de l'image  
Ligne 295 : Ligne 279 :
== Lancer notre nouvelle application ==
== Lancer notre nouvelle application ==


On va d'abord utiliser la commande :
On va d'abord utiliser la commande&nbsp;:
<pre class="highlight"><code>docker swarm init
<pre class="highlight">docker swarm init
</code></pre>
</pre>


Le fonctionnement de cette commande sera expliqué plus loin.
Le fonctionnement de cette commande sera expliqué plus loin.
Ligne 305 : Ligne 289 :
Si j'ai plusieurs adresses sur mon interface réseau (IPv6...) je peux en préciser une en ajoutant --advertise-addr @IP
Si j'ai plusieurs adresses sur mon interface réseau (IPv6...) je peux en préciser une en ajoutant --advertise-addr @IP


Je peux ensuite créer l'application :
Je peux ensuite créer l'application&nbsp;:
<pre class="highlight"><code>docker stack deploy <span class="nt">-c</span> docker-compose.yml monservicetest</code></pre>
<pre class="highlight">docker stack deploy docker-compose.yml monservicetest</pre>


Je peux remplacer "monservicetest" par n'importe quel nom.
Je peux remplacer "monservicetest" par n'importe quel nom.


Désormais, notre service fait tourner 5 réplique de notre image sur le même hôte. On peut faire un docker service ls et constater que celui-ci est bien présent :
Désormais, notre service fait tourner 5 réplique de notre image sur le même hôte. On peut faire un docker service ls et constater que celui-ci est bien présent&nbsp;:
<div style="background:#eeeeee; border:1px solid #cccccc; padding:5px 10px">
<div style="background:#eeeeee; border:1px solid #cccccc; padding:5px 10px">
justine@Justine-pc:~/Dockertest$ docker service ls<br/> ID&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NAME&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MODE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; REPLICAS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IMAGE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PORTS<br/> c3jwhxzgvjj6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; monservicetest_web&nbsp;&nbsp; replicated&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5/5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; justinep/monrepotest:letest&nbsp;&nbsp; *:4000->80/tcp
justine@Justine-pc:~/Dockertest$ docker service ls<br/> ID&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NAME&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MODE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; REPLICAS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IMAGE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PORTS<br/> c3jwhxzgvjj6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; monservicetest_web&nbsp;&nbsp; replicated&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5/5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; justinep/monrepotest:letest&nbsp;&nbsp; *:4000->80/tcp
</div>  
</div>  
&nbsp;On a un certain nombre d'infos : un ID de service, le nom avec le suffixe _web, les répliques, etc.
&nbsp;On a un certain nombre d'infos&nbsp;: un ID de service, le nom avec le suffixe _web, les répliques, etc.


Un seul conteneur tournant au sein d'un service est appellé une tâche (task). Les tâches ont un ID unique qui s'incrémente avec le nombre de répliques. Je peux les lister :
Un seul conteneur tournant au sein d'un service est appellé une tâche (task). Les tâches ont un ID unique qui s'incrémente avec le nombre de répliques. Je peux les lister&nbsp;:
<div style="background:#eeeeee; border:1px solid #cccccc; padding:5px 10px">justine@Justine-pc:~/Dockertest$ docker service ps monservicetest_webID&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NAME&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IMAGE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NODE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DESIRED STATE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CURRENT STATE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ERROR&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PORTS<br/> n4kztr59mt8o&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; monservicetest_web.1&nbsp;&nbsp; justinep/monrepotest:letest&nbsp;&nbsp; Justine-pc&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Running&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Running 4 minutes ago&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<br/> by570g9v7nzn&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; monservicetest_web.2&nbsp;&nbsp; justinep/monrepotest:letest&nbsp;&nbsp; Justine-pc&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Running&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Running 4 minutes ago&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<br/> k8mdckf6m8fg&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; monservicetest_web.3&nbsp;&nbsp; justinep/monrepotest:letest&nbsp;&nbsp; Justine-pc&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Running&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Running 4 minutes ago&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<br/> 84vxmgbbe1s0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; monservicetest_web.4&nbsp;&nbsp; justinep/monrepotest:letest&nbsp;&nbsp; Justine-pc&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Running&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Running 4 minutes ago&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<br/> jigcz7c22hdh&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; monservicetest_web.5&nbsp;&nbsp; justinep/monrepotest:letest&nbsp;&nbsp; Justine-pc&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Running&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Running 4 minutes ago</div>  
<div style="background:#eeeeee; border:1px solid #cccccc; padding:5px 10px">justine@Justine-pc:~/Dockertest$ docker service ps monservicetest_webID&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NAME&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IMAGE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NODE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DESIRED STATE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CURRENT STATE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ERROR&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PORTS<br/> n4kztr59mt8o&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; monservicetest_web.1&nbsp;&nbsp; justinep/monrepotest:letest&nbsp;&nbsp; Justine-pc&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Running&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Running 4 minutes ago&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<br/> by570g9v7nzn&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; monservicetest_web.2&nbsp;&nbsp; justinep/monrepotest:letest&nbsp;&nbsp; Justine-pc&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Running&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Running 4 minutes ago&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<br/> k8mdckf6m8fg&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; monservicetest_web.3&nbsp;&nbsp; justinep/monrepotest:letest&nbsp;&nbsp; Justine-pc&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Running&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Running 4 minutes ago&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<br/> 84vxmgbbe1s0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; monservicetest_web.4&nbsp;&nbsp; justinep/monrepotest:letest&nbsp;&nbsp; Justine-pc&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Running&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Running 4 minutes ago&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<br/> jigcz7c22hdh&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; monservicetest_web.5&nbsp;&nbsp; justinep/monrepotest:letest&nbsp;&nbsp; Justine-pc&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Running&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Running 4 minutes ago</div>  
On peut aussi les voir avec docker container ls, mais sans indications sur le service.
On peut aussi les voir avec docker container ls, mais sans indications sur le service.


Ligne 324 : Ligne 308 :
== Faire du scaling de notre application ==
== Faire du scaling de notre application ==


Il suffit de changer de changer le nombre de répliques dans le fichier docker-compose.yml, et de relancer le déploiement. Le service sera mis à jour, pas besoin de le couper avant : on peut rajouter des répliques.
Il suffit de changer de changer le nombre de répliques dans le fichier docker-compose.yml, et de relancer le déploiement. Le service sera mis à jour, pas besoin de le couper avant&nbsp;: on peut rajouter des répliques.


== Arrêter le swarm et l'application ==
== Arrêter le swarm et l'application ==
Ligne 332 : Ligne 316 :


Couper le swarm:
Couper le swarm:
<pre class="highlight"><code>docker swarm leave --force</code></pre>
<pre class="highlight">docker swarm leave --force</pre>


Note : les fichiers composés ainsi servent à définir des applications Docker, et peuvent être uploadés sur le cloud quand on utiliser Docker édition entreprise.
Note&nbsp;: les fichiers composés ainsi servent à définir des applications Docker, et peuvent être uploadés sur le cloud quand on utiliser Docker édition entreprise.


&nbsp;
Résumé des commandes :
<pre class="highlight"><code>docker stack <span class="nb">ls</span>                                            <span class="c"># List stacks or apps</span>
docker stack deploy <span class="nt">-c</span> <composefile> <appname>  <span class="c"># Run the specified Compose file</span>
docker service <span class="nb">ls</span>                <span class="c"># List running services associated with an app</span>
docker service ps <service>                  <span class="c"># List tasks associated with an app</span>
docker inspect <task or container>                  <span class="c"># Inspect task or container</span>
docker container <span class="nb">ls</span> <span class="nt">-q</span>                                      <span class="c"># List container IDs</span>
docker stack rm <appname>                            <span class="c"># Tear down an application</span>
docker swarm leave <span class="nt">--force</span>      <span class="c"># Take down a single node swarm from the manager</span>
</code></pre>


&nbsp;
&nbsp;

Version du 14 novembre 2018 à 22:38

Les notes de cettes pages sont prises à partir de la documentation de docker : https://docs.docker.com/get-started/  

Présentation de Docker

Docker est une plateforme qui permet de développer, déployer et faire tourner des applications en conteneurs. L'usage de conteneur s'appelle la conteneurisation. Les conteneurs sont:

  • Flexibles : Tout peut être conteneurisé
  • Légers
  • Interchangeables
  • Portables
  • Scalables
  • Empilables

Un conteneur est lancé en faisant tourner une image : une image est un exécutable qui inclut tout ce dont a besoin le programme, et le conteneur est l'environnement d'exécution qui permet à cette image de fonctionner. Une fois lancé le conteneur est un processus utilisateur, que l'on peut lister avec docker ps. Les conteneurs partagent entre eux les ressources système et le noyau Linux. 

À la différence d'une VM, un conteneur peut tourner nativement sur n'importe quelle machine Linux; une même machine Linux peut utiliser plusieurs conteneurs, cette technologie est plus économe que les VM.

Préparer l'environnement Docker

On va commencer par installer Docker.

https://docs.docker.com/install/

Le versioning fonctionne sur le modèle : Année.Mois.Patch

Une fois installé, on peut tester avec docker --version.

On a plus d'informations avec docker info:

justine@Justine-pc:~$ docker info
Containers: 0
 Running: 0
 Paused: 0
 Stopped: 0
Images: 1
Server Version: 18.09.0
Storage Driver: overlay2
 Backing Filesystem: extfs
 Supports d_type: true
 Native Overlay Diff: true
Logging Driver: json-file
Cgroup Driver: cgroupfs
Plugins:
 Volume: local
 Network: bridge host macvlan null overlay
 Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
Swarm: inactive
Runtimes: runc
Default Runtime: runc
Init Binary: docker-init
containerd version: d6de12e2f362cb9dc49ad957911996d3de59b338
runc version: 4fc53a81fb7c994640722ac585fa9ca548971871
init version: fec3683
Security Options:
 apparmor
 seccomp
  Profile: default
Kernel Version: 4.18.0-10-generic
Operating System: Ubuntu 18.10
OSType: linux
Architecture: x86_64
CPUs: 8
Total Memory: 15.59GiB
Name: ***
ID: EN3D:SYGK:2WYA:TZM5:MHRP:FBOH:GQVD:VFKW:SDG6:V3FW:XSKK:5MAI
Docker Root Dir: /var/lib/docker
Debug Mode (client): false
Debug Mode (server): false
Registry: https://index.docker.io/v1/
Labels:
Experimental: false
Insecure Registries:
 127.0.0.0/8
Live Restore Enabled: false
Product License: Community Engine

WARNING: No swap limit support

On peut ensuite ajouter son utilisateur au groupe docker:

sudo adduser user docker

 

Tester l'installation de Docker

Le test le plus simple consiste à faire tourner l'image hello-world :

justine@Justine-pc:~$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
d1725b59e92d: Pull complete
Digest: sha256:0add3ace90ecb4adbf7777e9aacf18357296e799f81cabc9fde470971e499788
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

 

Hello-world est une image simple qui permet de valider l'installation.

On peut lister les images télechargées avec docker image ls

...et lister les conteneurs avec docker container ls --all

Récapitulatif des commandes issu de la documentation Docker :

List Docker CLI commands
docker
docker container --help

 

Display Docker version and info
docker --version
docker version
docker info

 

Execute Docker image
docker run hello-world

 

List Docker images
docker image ls

 

List Docker containers (running, all, all in quiet mode)
docker container ls
docker container ls --all
docker container ls -aq

 

 

Construire une app

Nous allons commencer en bas de la hiérarchie de Docker pour remonter. La hiérarchie Docker est constituée comme suit :

-Stack : Définit les interactions entre les services
-Services : Définit comment les containers se comportent en production
-Conteneur : On y est.

Classiquement, lorsque l'on construit une application en python par exemple, on commence par télécharger un environnement Python pour ensuite coder par dessus. Avec Docker, on peut se contenter de récupérer une image contenant une instance de Python sans installation. Ensuite notre build contiendra l'image de Python avec toutes ses dépendances, ainsi que notre application; tout sera "packagé" ensemble, et on sera sûrs que tout fonctionne. Cela évite d'avoir à prendre en compte des différences entre l'environnement de développement et celui de production.

Ces images portables sont définies par un fichier de configuration appellé Dockerfile.

Définir un container avec Dockerfile

Le Dockerfil définit tout ce qui va aller dans l'environnement à l'intérieur du conteneur; les ressources comme les interfaces réseau sont virtualisées dans cet environnement isolé, alors il va falloir mapper les ports vers le monde extérieur, et être spécifique quant aux fichiers que l'on veut copier dans l'environnement. Après ça, le conteneur devrait fonctionner partout.

On va commencer avec Python. Il faut créer un nouveau dossier, s'y rendre, créer un fichier appellé "Dockerfile" et y coller ce texte :

<code><span class="c"># Use an official Python runtime as a parent image</span>
<span class="k">FROM</span><span class="s"> python:2.7-slim</span>

<span class="c"># Set the working directory to /app</span>
<span class="k">WORKDIR</span><span class="s"> /app</span>

<span class="c"># Copy the current directory contents into the container at /app</span>
<span class="k">COPY</span><span class="s"> . /app</span>

<span class="c"># Install any needed packages specified in requirements.txt</span>
<span class="k">RUN </span>pip install <span class="nt">--trusted-host</span> pypi.python.org <span class="nt">-r</span> requirements.txt

<span class="c"># Make port 80 available to the world outside this container</span>
<span class="k">EXPOSE</span><span class="s"> 80</span>

<span class="c"># Define environment variable</span>
<span class="k">ENV</span><span class="s"> NAME World</span>

<span class="c"># Run app.py when the container launches</span>
<span class="k">CMD</span><span class="s"> ["python", "app.py"]</span>
</code>

Ce fichier fait référence à deux fichiers encore inexistants, app.py et requirements.txt. On va les créer.

L'application proprement dite

Dans le dossier lui-même, créer les deux fichiers.

Notre app sera alors complète: puisque que le Dockerfile contient la commande COPY, app.py et requirements.txt seront pris en compte à la création de l'image. La sortie de l'application sera dispo via http grâce au EXPOSE, qui permet d'accéder à la sortie depuis le "monde extérieur" sur le port 80.

requirements.txt:

<code>Flask
Redis</code>

app.py:

<code><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span>
<span class="kn">from</span> <span class="nn">redis</span> <span class="kn">import</span> <span class="n">Redis</span><span class="p">,</span> <span class="n">RedisError</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">socket</span>

<span class="c"># Connect to Redis</span>
<span class="n">redis</span> <span class="o">=</span> <span class="n">Redis</span><span class="p">(</span><span class="n">host</span><span class="o">=</span><span class="s">"redis"</span><span class="p">,</span> <span class="n">db</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">socket_connect_timeout</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">socket_timeout</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>

<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>

<span class="nd">@app.route</span><span class="p">(</span><span class="s">"/"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">hello</span><span class="p">():</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="n">visits</span> <span class="o">=</span> <span class="n">redis</span><span class="o">.</span><span class="n">incr</span><span class="p">(</span><span class="s">"counter"</span><span class="p">)</span>
    <span class="k">except</span> <span class="n">RedisError</span><span class="p">:</span>
        <span class="n">visits</span> <span class="o">=</span> <span class="s">"<i>cannot connect to Redis, counter disabled</i>"</span>

    <span class="n">html</span> <span class="o">=</span> <span class="s">"<h3>Hello {name}!</h3>"</span> \
           <span class="s">"<b>Hostname:</b> {hostname}<br/>"</span> \
           <span class="s">"<b>Visits:</b> {visits}"</span>
    <span class="k">return</span> <span class="n">html</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s">"NAME"</span><span class="p">,</span> <span class="s">"world"</span><span class="p">),</span> <span class="n">hostname</span><span class="o">=</span><span class="n">socket</span><span class="o">.</span><span class="n">gethostname</span><span class="p">(),</span> <span class="n">visits</span><span class="o">=</span><span class="n">visits</span><span class="p">)</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
    <span class="n">app</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">host</span><span class="o">=</span><span class="s">'0.0.0.0'</span><span class="p">,</span> <span class="n">port</span><span class="o">=</span><span class="mi">80</span><span class="p">)</span>
</code>

 Ici le pip install -r requirements.txt installe les bibliothèques Flask et Redis pour Python. L'application affiche aussi la variable d'environnement NAME et la sortie d'un appel à socket.gethostname(). Ici, Redis ne devrait pas fonctionner puisqu'on a fait qu'installer sa bibliothèque, et pas Redis lui-même.

NB : Redis est un système de BDD "anti-SQL".

NB2 : demander le hostname depuis un conteneur renvoie le container ID, qui est comme le PID d'un exécutable.

Construire l'application

On est prêts à construire notre application ! Il faut s'assurer que l'on est dans notre dossier et qu'il contient bien nos trois fichiers (Dockerfile, requirements.txt et app.py). On va lancer la construction :

docker build -t montest .

L'argument -t permet de taguer mon image pour qu'elle ait un nom reconnaissable (ici "montest"); ne pas oublier le . qui renvoie au dossier actuel.

Docker va alors exécuter le Dockerfile; installer requirements.txt dans l'image en partant de l'image Python, etc...

Une fois le processus terminé, mon dossier n'as pas changé; mais l'image est bien apparu dans ma liste d'images, ce que je peux voir avec un docker image ls.

 

Problèmes de proxy/dns:

Un Proxy peut être indiqué dans le dockerfile avec:

<code><span class="c"># Set proxy server, replace host:port with values for your servers
</span><span class="n">ENV</span> <span class="n">http_proxy</span> <span class="n">host</span>:<span class="n">port</span>
<span class="n">ENV</span> <span class="n">https_proxy</span> <span class="n">host</span>:<span class="n">port</span>
</code>

Un DNS peut être indiqué à Docker en modifiant/créant le fichier /etc/docker/daemon.json et en y mettant:

<code><span class="p">{</span><span class="w">
  </span><span class="s2">"dns"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"your_dns_address"</span><span class="p">,</span><span class="w"> </span><span class="s2">"8.8.8.8"</span><span class="p">]</span><span class="w">
</span><span class="p">}</span></code>

Il faut ensuite sauvegarder le fichier et redémarrer le service docker.

Tester l'application

Notre image est créée, il est temps de créer ce conteneur et de tester !

il suffit d'un :

docker run -p 4000:80 montest

L'argument -p 4000:80 mappe le port 4000 de ma machine au port 80 dans le conteneur : j'ai alors accès à mon application en visitant l'url suivante : 127.0.0.1:4000 . 

J'y vois la sortie de mon application. Ca marche aussi avec un curl http://localhost:4000

On peut aussi lancer l'application en arrière-plan (detached) :

docker run -d -p 4000:80 montest

J'ai alors l'identifiant de conteneur et je suis renvoyée au terminal : le conteneur est en arrière-plan. Je le vois avec docker container ls : je vois que le container ID (en abrégé) correspond à ce que renvoie l'app.

Je peux stopper le conteneur avec :

docker container stop <containerID>

Partager l'image

On peut uploader cette image et la faire tourner ailleurs.

En effet il va être nécessaire de savoir comment push des registres lors de la mise en prod. Un registre est une collection de dépôts, et un dépôt est une collection d'images. Un peu comme Github, mais le code est déjà construit. On peut créer plusieurs dépôts sur un registre avec un seul compte; la ligne de commande docker utilise le registre public de docker par défaut. Il en existe d'autres.

On peut même créer son propre registre !

Se logger

Il va falloir se logger avec son compte docker (qu'on peut créer sur hub.docker.com) et noter son identifiant.

Une fois que c'est fait, je peux me connecter sur le site et créer un repo.

Je peux ensuite logger via le terminal avec docker login.

Tagger l'image

La notation pour associer une image a un dépôt est nomutilisateur/repo:tag. Le tag est optionnel mais recommandé, car c'est c'est le mécanisme utilisé par DOcker pour donner des numéros de versions. Il faut donc que les noms aient du sens.

Je vais donc faire ici :

docker tag montest justinep/monrepotest:letest #montest est l'image, justinep mon identifiant Docker, letest est le tag que je donne.

Un docker image ls me permet de voir mon image tagguée.

Publier l'image

Je peux uploader l'image avec :

docker push justinep/monrepotest:letest

Récupérer l'image depuis le dépôt et la faire tourner (Pull and Run)

Désormais, je peux faire un :

docker run -p 4000:80 justinep/monrepotest:letest

pour faire tourner l'image : si elle n'est pas sur ma machine locale, elle est téléchargée et lancée.

Peu importe où je suis, Docker récupère l'image avec Python et tout le reste, et fait tourner le code; tout ça dans un paquet bien ficelé, sans qu'il n'y ai rien de plus à installer.

Les Services

Nous allons scaler nos applications et active le load balancing. Pour cela, On va monter d'un niveau dans la hiérarchie pour voir les services.

Dans une application distribuée, les différentes parties de l'application sont appellées "services". Par exemple, si l'on prend un site web de streaming vidéo, l'un des services servirait à à stocker les données dans une BDD; un autre à encoder les vidéos; etc.

Les services ne sont finalement que des conteneurs en prod. Un service fait tourner une image, mais il codify la façon dont elle tourne : quels ports utiliser, combien de répliques du conteneur, etc. Le scaling d'un service change le nombre d'instances d'un conteneur seront lancées pour un même logiciel, afin d'assigner plus de ressources matérielles au processus. Les services peuvent être définis, lancés et scalés grâce au fichier docker-compose.yaml.

Notre premier docker-compose.yml

Ce fichier définit la façon dont les conteneurs doivent se comporter en production.

Une foit que l'on s'est assuré que notre image a bien été push, on peut créer le fichier n'importe où et y coller le contenu suivant :

version: "3"
services:
  web:
    # replace username/repo:tag with your name and image details
    image: username/repo:tag
    deploy:
      replicas: 5
      resources:
        limits:
          cpus: "0.1"
          memory: 50M
      restart_policy:
        condition: on-failure
    ports:
      - "4000:80"
    networks:
      - webnet
networks:
  webnet:

 

Ce fichier accomplit les choses suivantes :

  • Faire un pull de l'image
  • Lancer 5 instances de l'image en tant qu'un service appellé "web", chacun pouvant utiliser maximum 10% du CPU (distribué sur tous les coeurs) et 50 Mo de RAM
  • Relancer immédiatement un conteneur qui plante
  • Mapper le port 4000 de l'hôte au port 80 de web
  • Dire aux conteneur de web de se partager le port 80 via un réseau avec load-balancing appellé webnet
  • Définir le réseau webnet avec les paramètres par défaut (un réseau avec load-balancing)

Lancer notre nouvelle application

On va d'abord utiliser la commande :

docker swarm init

Le fonctionnement de cette commande sera expliqué plus loin.

NB: Un swarm est un outil de clustering et de scheduling servant à administrer un cluster de noeuds Docker comme si c'était un seul système virtuel.

Si j'ai plusieurs adresses sur mon interface réseau (IPv6...) je peux en préciser une en ajoutant --advertise-addr @IP

Je peux ensuite créer l'application :

docker stack deploy docker-compose.yml monservicetest

Je peux remplacer "monservicetest" par n'importe quel nom.

Désormais, notre service fait tourner 5 réplique de notre image sur le même hôte. On peut faire un docker service ls et constater que celui-ci est bien présent :

justine@Justine-pc:~/Dockertest$ docker service ls
ID                  NAME                 MODE                REPLICAS            IMAGE                         PORTS
c3jwhxzgvjj6        monservicetest_web   replicated          5/5                 justinep/monrepotest:letest   *:4000->80/tcp

 On a un certain nombre d'infos : un ID de service, le nom avec le suffixe _web, les répliques, etc.

Un seul conteneur tournant au sein d'un service est appellé une tâche (task). Les tâches ont un ID unique qui s'incrémente avec le nombre de répliques. Je peux les lister :

justine@Justine-pc:~/Dockertest$ docker service ps monservicetest_webID                  NAME                   IMAGE                         NODE                DESIRED STATE       CURRENT STATE           ERROR               PORTS
n4kztr59mt8o        monservicetest_web.1   justinep/monrepotest:letest   Justine-pc          Running             Running 4 minutes ago                       
by570g9v7nzn        monservicetest_web.2   justinep/monrepotest:letest   Justine-pc          Running             Running 4 minutes ago                       
k8mdckf6m8fg        monservicetest_web.3   justinep/monrepotest:letest   Justine-pc          Running             Running 4 minutes ago                       
84vxmgbbe1s0        monservicetest_web.4   justinep/monrepotest:letest   Justine-pc          Running             Running 4 minutes ago                       
jigcz7c22hdh        monservicetest_web.5   justinep/monrepotest:letest   Justine-pc          Running             Running 4 minutes ago

On peut aussi les voir avec docker container ls, mais sans indications sur le service.

Si je vais voir mon 127.0.0.1:4000 et que je rafraichis la page plusieurs fois, je vois que le hostname renvoyé change, grâce au load balancing.

Faire du scaling de notre application

Il suffit de changer de changer le nombre de répliques dans le fichier docker-compose.yml, et de relancer le déploiement. Le service sera mis à jour, pas besoin de le couper avant : on peut rajouter des répliques.

Arrêter le swarm et l'application

On peut arrêter l'app avec:

<code>docker stack rm monservicetest</code>

Couper le swarm:

docker swarm leave --force

Note : les fichiers composés ainsi servent à définir des applications Docker, et peuvent être uploadés sur le cloud quand on utiliser Docker édition entreprise.

Résumé des commandes :

<code>docker stack <span class="nb">ls</span>                                            <span class="c"># List stacks or apps</span>
docker stack deploy <span class="nt">-c</span> <composefile> <appname>  <span class="c"># Run the specified Compose file</span>
docker service <span class="nb">ls</span>                 <span class="c"># List running services associated with an app</span>
docker service ps <service>                  <span class="c"># List tasks associated with an app</span>
docker inspect <task or container>                   <span class="c"># Inspect task or container</span>
docker container <span class="nb">ls</span> <span class="nt">-q</span>                                      <span class="c"># List container IDs</span>
docker stack rm <appname>                             <span class="c"># Tear down an application</span>
docker swarm leave <span class="nt">--force</span>      <span class="c"># Take down a single node swarm from the manager</span>
</code>