Comment déboguer et résoudre les problèmes courants de Docker

introduction

Docker simplifie l’enveloppement de vos applications et services dans des conteneurs afin de pouvoir les exécuter n’importe où. Malheureusement, des problèmes peuvent survenir lors de la création de votre image et de l’intégration de toutes les couches dont votre application a besoin, notamment si vous connaissez bien les images et les conteneurs Docker. Vous pouvez rencontrer des erreurs de frappe, des problèmes avec les bibliothèques et les modules d’exécution, des conflits de noms, des problèmes lors de la communication avec d’autres conteneurs.

Dans ce guide de dépannage destiné aux nouveaux utilisateurs de Docker, vous allez résoudre les problèmes lors de la création d’images Docker, résoudre les conflits de nommage lors de l’exécution de conteneurs et résoudre les problèmes pouvant survenir lors de la communication entre les conteneurs.

Conditions préalables

Pour compléter ce tutoriel, vous aurez besoin de

  • Docker installé sur un serveur ou sur votre ordinateur local.

Pour installer Docker sur un serveur, vous pouvez suivre les guides pratiques for CentOS 7. ou https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-16-04 pour Ubuntu 16.04].

Vous pouvez visiter le site Web Docker ou suivre la documentation d’installation officielle pour installer Docker sur votre ordinateur local. machine.

Étape 1 - Résolution des problèmes avec le fichier Docker

Les problèmes les plus fréquents peuvent se produire lorsque vous construisez votre image Docker à partir d’un fichier + Dockerfile. Avant de plonger, clarifions la différence entre images et conteneurs.

  • Un image est une ressource en lecture seule que vous créez à l’aide d’un fichier de configuration appelé + Dockerfile +. C’est ce que vous expédiez et partagez via http://dockerhub.com [Docker Hub] ou votre registre privé.

  • Un conteneur est une instance de lecture et d’écriture créée à partir de l’image que vous avez construite.

Pour en savoir plus sur ces concepts, consultez le tutoriel Docker expliqué: Utilisation de Dockerfiles pour automatiser la construction d’images.

Lorsque vous regardez un + Dockerfile +, vous pouvez clairement voir le processus pas à pas utilisé par Docker pour construire l’image, car chaque ligne du + Dockerfile + correspond à une étape du processus. Cela signifie généralement que si vous atteignez une certaine étape, toutes les étapes précédentes sont terminées avec succès.

Créons un petit projet pour explorer les problèmes que vous pourriez rencontrer avec un fichier + Dockerfile +. Créez un répertoire + docker_image + dans votre répertoire personnel et utilisez + nano + ou votre éditeur favori pour créer un + Dockerfile + dans ce dossier.

mkdir ~/docker_image
nano ~/docker_image/Dockerfile

Ajoutez le contenu suivant à ce nouveau fichier:

~ / docker_image / Dockerfile

# base image
FROM debian:latest

# install basic apps
RUN aapt-get install -qy nano

Il y a une faute de frappe intentionnelle dans ce code. Peux tu le repérer? Essayez de créer une image à partir de ce fichier pour voir comment Docker gère une commande incorrecte. Créez l’image avec la commande suivante:

docker build -t my_image ~/docker_image

Vous verrez ce message sur votre terminal, indiquant une erreur:

OutputStep 2 : RUN aapt-get install -qy nano
 ---> Running in 085fa10ffcc2

Le message d’erreur à la fin signifie qu’il y a eu un problème avec la commande à l’étape 2. Dans ce cas, c’était notre faute de frappe intentionnelle: nous avons + aapt-get + au lieu de + apt-get +. Mais cela signifiait également que l’étape précédente s’était exécutée correctement.

Modifiez le + Dockerfile + et apportez la correction:

Dockerfile

# install basic apps
RUN  install -qy nano

Maintenant, exécutez à nouveau la commande + docker build:

docker build -t my_image ~/docker_image

Et maintenant, vous verrez la sortie suivante:

OutputSending build context to Docker daemon 2.048 kB
Step 1 : FROM debian:latest
---> ddf73f48a05d
Step 2 : RUN apt-get install -qy nano
---> Running in 9679323b942f
Reading package lists...
Building dependency tree...

Une fois la typo corrigée, le processus a été un peu plus rapide, car Docker a mis en cache la première étape au lieu de retélécharger l’image de base. Mais comme vous pouvez le constater à la sortie, nous avons une nouvelle erreur.

La distribution Debian sur laquelle nous avons fondé notre image n’a pas pu trouver l’éditeur de texte + nano +, bien que nous sachions qu’il est disponible dans les dépôts de paquets Debian. L’image de base est livrée avec des métadonnées en cache, telles que des référentiels et des listes de packages disponibles. Il est possible que vous rencontriez parfois des problèmes de cache lorsque les référentiels en direct dont vous extrayez des données ont changé.

Pour résoudre ce problème, modifiez le fichier Dockerfile pour effectuer un nettoyage et une mise à jour des sources avant d’installer de nouveaux packages. Ouvrez à nouveau le fichier de configuration:

nano ~/docker_image/Dockerfile

Ajoutez la ligne en surbrillance suivante au fichier, above la commande pour installer + nano +:

~ / docker_image / Dockerfile

# base image
FROM debian:latest

# clean and update sources


# install basic apps
RUN apt-get install -qy nano

Enregistrez le fichier et exécutez à nouveau la commande + docker build:

docker build -t my_image ~/docker_image

Cette fois, le processus se termine avec succès.

OutputSending build context to Docker daemon 2.048 kB
Step 1 : FROM debian:latest
---> a24c3183e910
Step 2 : RUN apt-get install -qy nano
---> Running in 2237d254f172
Reading package lists...
Building dependency tree...
Reading state information...
Suggested packages:
 spell
The following NEW packages will be installed:
 nano
...

---> 64ff1d3d71d6
Removing intermediate container 2237d254f172
Successfully built 64ff1d3d71d6

Voyons ce qui se passe lorsque nous ajoutons Python 3 et le pilote PostgreSQL à notre image. Ouvrez à nouveau le fichier + Dockerfile +.

nano ~/docker_image/Dockerfile

Et ajoutez deux nouvelles étapes pour installer Python 3 et le pilote Python PostgreSQL:

~ / docker_image / Dockerfile

# base image
FROM debian:latest

# clean and update sources
RUN apt-get clean && apt-get update

# install basic apps
RUN apt-get install -qy nano

# install Python and modules

Enregistrez le fichier, quittez l’éditeur et reconstruisez l’image à nouveau:

docker build -t my_image ~/docker_image

Comme vous pouvez le voir à la sortie, les packages s’installent correctement. Le processus se termine également beaucoup plus rapidement car les étapes précédentes ont été mises en cache.

OutputSending build context to Docker daemon 2.048 kB
Step 1 : FROM debian:latest
---> ddf73f48a05d
Step 2 : RUN apt-get clean && apt-get update
---> Using cache
---> 2c5013476fbf
Step 3 : RUN apt-get install -qy nano
---> Using cache
---> 4b77ac535cca
Step 4 : RUN apt-get install -qy python3
---> Running in 93f2d795fefc
Reading package lists...
Building dependency tree...
Reading state information...
The following extra packages will be installed:
 krb5-locales libgmp10 libgnutls-deb0-28 libgssapi-krb5-2 libhogweed2
 libk5crypto3 libkeyutils1 libkrb5-3 libkrb5support0 libldap-2.4-2 libnettle4
 libp11-kit0 libpq5 libsasl2-2 libsasl2-modules libsasl2-modules-db
 libtasn1-6
Suggested packages:
 gnutls-bin krb5-doc krb5-user libsasl2-modules-otp libsasl2-modules-ldap
 libsasl2-modules-sql libsasl2-modules-gssapi-mit
 libsasl2-modules-gssapi-heimdal python-psycopg2-doc
The following NEW packages will be installed:
 krb5-locales libgmp10 libgnutls-deb0-28 libgssapi-krb5-2 libhogweed2
 libk5crypto3 libkeyutils1 libkrb5-3 libkrb5support0 libldap-2.4-2 libnettle4
 libp11-kit0 libpq5 libsasl2-2 libsasl2-modules libsasl2-modules-db
 libtasn1-6 python3-psycopg2
0 upgraded, 18 newly installed, 0 to remove and 0 not upgraded.
Need to get 5416 kB of archives.
After this operation, 10.4 MB of additional disk space will be used.

...

Processing triggers for libc-bin (2.19-18+deb8u6) ...
---> 978e0fa7afa7

Portez une attention particulière à la sortie de Docker pour identifier où se trouvent les fautes de frappe et exécutez les mises à jour au moment de la construction et à l’intérieur du conteneur pour vous assurer que les listes de paquets en cache ne vous gênent pas.

Les erreurs de syntaxe et les problèmes de mise en cache sont les problèmes les plus courants que vous pouvez rencontrer lors de la création d’une image dans Docker. Examinons maintenant les problèmes pouvant survenir lors de l’exécution de conteneurs à partir de ces images.

Étape 2 - Résolution des problèmes de dénomination des conteneurs

Lorsque vous lancerez plus de conteneurs, vous rencontrerez éventuellement des collisions de noms. Une collision de noms survient lorsque vous essayez de créer un conteneur qui porte le même nom qu’un conteneur existant déjà sur votre système. Voyons maintenant comment gérer correctement le nommage, le changement de nom et la suppression des conteneurs afin d’éviter les collisions.

Lançons un conteneur à partir de l’image construite à la section précédente. Nous allons exécuter un interpréteur interactif bash à l’intérieur de ce conteneur pour tester les choses. Exécutez la commande suivante:

docker run -ti my_image bash

Lorsque le conteneur démarre, une invite de la racine vous attend pour obtenir des instructions:

Maintenant que vous avez un conteneur en cours d’exécution, voyons quels types de problèmes vous pourriez rencontrer.

Lorsque vous exécutez un conteneur comme vous venez de le faire, sans définir explicitement de nom, Docker attribue un nom aléatoire au conteneur. Vous pouvez voir tous les conteneurs en cours d’exécution et leurs noms correspondants en exécutant la commande + docker ps + sur l’hôte Docker, en dehors du conteneur en cours d’exécution.

Ouvrez un nouveau terminal sur l’hôte Docker et exécutez la commande suivante:

docker ps

Cette commande affiche la liste des conteneurs en cours d’exécution avec leur nom, comme indiqué dans l’exemple suivant:

OutputCONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
80a0ca58d6ec        my_image            "bash"              22 seconds ago      Up 28 seconds

Le nom + loving_brahmagupta + dans la sortie précédente est le nom que Docker a automatiquement attribué au conteneur dans l’exemple précédent; le vôtre aura un nom différent. Laisser Docker attribuer un nom à votre conteneur convient parfaitement dans des cas très simples, mais peut présenter des problèmes importants; lorsque nous déployons, nous devons nommer les conteneurs de manière cohérente afin de pouvoir les référencer et les automatiser facilement.

Pour spécifier un nom pour un conteneur, nous pouvons utiliser l’argument + - name + lorsque nous lançons le conteneur ou renommer un conteneur en cours d’exécution en un nom plus descriptif.

Exécutez la commande suivante à partir du terminal de l’hôte Docker:

docker rename  python_box

Puis listez vos conteneurs:

docker ps

Vous verrez le conteneur + python_box + dans la sortie, confirmant que vous avez renommé le conteneur avec succès:

OutputCONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
80a0ca58d6ec        my_image            "bash"              24 minutes ago      Up 24 minutes                           python_box

Pour fermer le conteneur, tapez + exit + à l’invite du terminal contenant le conteneur en cours d’exécution:

exit

Si ce n’est pas une option, vous pouvez supprimer le conteneur d’un autre terminal de l’hôte Docker à l’aide de la commande suivante:

docker kill python_box

Lorsque vous tuez le conteneur de cette manière, Docker renvoie le nom du conteneur qui vient d’être tué:

Outputpython_box

Pour vous assurer que + python_box + n’existe plus, listez à nouveau tous les conteneurs en cours d’exécution:

docker ps

Comme prévu, le conteneur n’est plus répertorié:

OutputCONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

Vous pouvez maintenant penser que vous pourriez lancer un autre conteneur nommé + python_box +, mais voyons ce qui se passe lorsque nous essayons.

Nous allons utiliser l’argument + - name + cette fois pour définir le nom du conteneur:

docker run  -ti my_image bash
Outputdocker: Error response from daemon: Conflict. The name "/python_box" is already in use by container 80a0ca58d6ecc80b305463aff2a68c4cbe36f7bda15e680651830fc5f9dda772. You have to remove (or rename) that container to be able to reuse that name..
See 'docker run --help'.

Lorsque vous créez une image et réutilisez le nom d’une image existante, celle-ci est écrasée, comme vous l’avez déjà vu. Les conteneurs sont un peu plus compliqués, car vous ne pouvez pas écraser un conteneur qui existe déjà.

Docker dit que "+ python_box " existe déjà, même si nous venons de le tuer et qu'il n'est même pas listé avec " docker ps ". Il ne fonctionne pas, mais il reste disponible au cas où vous voudriez le redémarrer. Nous l’avons arrêté, mais nous ne l’avons pas enlevé. La commande ` docker ps +` affiche uniquement les conteneurs running, pas les conteneurs all.

Pour lister all des conteneurs Docker, en cours d’exécution ou autrement, passez l’indicateur + -a + (alias pour + - all +) à + ​​docker ps +:

docker ps -a

Notre conteneur + python_box + apparaît maintenant dans la sortie:

OutputCONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                        PORTS               NAMES
80a0ca58d6ec        my_image            "bash"              12 minutes ago      Exited (137) 6 minutes ago

Le conteneur existe avec un statut + Exited (137) +, raison pour laquelle nous avons rencontré un problème de dénomination lorsque nous avons essayé de créer un nouveau conteneur portant le même nom.

Lorsque vous souhaitez supprimer complètement un conteneur, vous utilisez la commande + docker rm +. Exécutez cette commande dans votre terminal:

docker rm python_box

Une fois encore, Docker affiche le nom du conteneur qui vient d’être supprimé:

Outputpython_box

Créons un nouveau conteneur nommé + python_box + maintenant que nous avons supprimé le précédent:

docker run --name python_box -ti my_image bash

Le processus est terminé et nous sommes à nouveau présentés avec un shell root:

Supprimons maintenant et retirons le conteneur afin d’éviter des problèmes à l’avenir. A partir d’une autre session Terminal sur l’hôte Docker, supprimez le conteneur et supprimez-le à l’aide de la commande suivante:

docker kill python_box && docker rm python_box

Nous avons chaîné deux commandes ensemble, de sorte que la sortie affiche le nom du conteneur deux fois. La première sortie vérifie que nous avons tué le conteneur et l’autre confirme que nous l’avons retiré.

Outputpython_box
python_box

Gardez à l’esprit + docker ps -a + lorsque vous rencontrez des problèmes de noms et assurez-vous que vos conteneurs sont arrêtés et supprimés avant d’essayer de les recréer avec le même nom.

Nommer les conteneurs facilite la gestion de votre infrastructure. Les noms facilitent également la communication entre les conteneurs, comme vous le verrez plus loin.

Étape 3 - Résolution des problèmes de communication du conteneur

Docker facilite l’instanciation de plusieurs conteneurs afin que vous puissiez exécuter des services différents, voire redondants, dans chacun d’eux. Si un service échoue ou est compromis, vous pouvez simplement le remplacer par un nouveau tout en préservant l’intégrité du reste de l’infrastructure. Mais vous pouvez rencontrer des problèmes pour que ces conteneurs communiquent les uns avec les autres.

Créons deux conteneurs qui communiquent afin d’explorer les problèmes de communication potentiels. Nous allons créer un conteneur exécutant Python en utilisant notre image existante et un autre conteneur exécutant une instance de PostgreSQL. Nous allons utiliser l’image officielle de PostgreSQL disponible sur http://dockerhub.com [Docker Hub] pour ce conteneur.

Commençons par créer le conteneur PostgreSQL. Nous allons attribuer un nom à ce conteneur à l’aide de l’indicateur + - name + afin de l’identifier facilement lors de la liaison avec d’autres conteneurs. Nous l’appellerons + postgres_box +.

Auparavant, lorsque nous avons lancé un conteneur, celui-ci apparaissait au premier plan, prenant en charge notre terminal. Nous voulons démarrer le conteneur de base de données PostgreSQL en arrière-plan, ce que nous pouvons faire avec l’indicateur + - detach +.

Enfin, au lieu d’exécuter + bash +, nous allons exécuter la commande + postgres + qui démarrera le serveur de base de données PostgreSQL à l’intérieur du conteneur.

Exécutez la commande suivante pour lancer le conteneur:

docker run --name postgres_box --detach postgres

Docker téléchargera l’image à partir de Docker Hub et créera le conteneur. Il retournera ensuite l’identifiant complet du conteneur s’exécutant en arrière-plan:

OutputUnable to find image 'postgres:latest' locally
latest: Pulling from library/postgres
6a5a5368e0c2: Already exists
193f770cec44: Pull complete
...
484ac0d6f901: Pull complete
Digest: sha256:924650288891ce2e603c4bbe8491e7fa28d43a3fc792e302222a938ff4e6a349
Status: Downloaded newer image for postgres:latest

Répertoriez les conteneurs pour vous assurer que ce nouveau conteneur est en cours d’exécution:

docker ps

La sortie confirme que le conteneur + postgres_box + s’exécute en arrière-plan, exposant le port + 5432 +, le port de la base de données PostgreSQL:

OutputCONTAINER ID        IMAGE               COMMAND                  CREATED                  STATUS              PORTS               NAMES
7a230b56cd64                    "/docker-entrypoint.s"   Less than a second ago   Up 2 seconds

Lançons maintenant le conteneur Python. Pour que les programmes s’exécutant dans le conteneur Python «voient» les services dans le conteneur + postgres_box +, nous devons lier manuellement notre conteneur Python au conteneur + postgres_box + en utilisant l’argument + - link +. Pour créer le lien, nous spécifions le nom du conteneur, suivi du nom du lien. Nous utiliserons le nom du lien pour faire référence au conteneur + postgres_box + depuis le conteneur Python.

Exécutez la commande suivante pour démarrer le conteneur Python:

docker run --name python_box --link  -ti my_image bash

Essayons maintenant de vous connecter à PostgreSQL depuis le conteneur + python_box +.

Nous avions précédemment installé + nano + à l’intérieur du conteneur + python_box +, nous allons donc l’utiliser pour créer un simple script Python afin de tester la connexion à PostgreSQL. Dans le terminal pour le conteneur + python_box +, exécutez cette commande:

nano pg_test.py

Ajoutez ensuite le script Python suivant au fichier:

pg_test.py

"""Test PostgreSQL connection."""
import psycopg2

conn = psycopg2.connect(user='postgres')
print(conn)

Enregistrez le fichier et quittez l’éditeur. Voyons ce qui se passe lorsque nous essayons de nous connecter à la base de données à partir de notre script. Exécutez le script dans votre conteneur:

python3 pg_test.py

Le résultat affiché indique un problème de connexion à la base de données:

OutputTraceback (most recent call last):
 File "pg_test.py", line 5, in <module>
   conn = psycopg2.connect(database="test", user="postgres", password="secret")
 File "/usr/lib/python3/dist-packages/psycopg2/__init__.py", line 164, in connect
   conn = _connect(dsn, connection_factory=connection_factory, async=async)
psycopg2.OperationalError:

Nous nous sommes assurés que le conteneur + postgres_box + est en cours d’exécution et nous l’avons lié au conteneur + python_box +, que s’est-il passé alors? Eh bien, nous n’avons jamais spécifié l’hôte de la base de données lorsque nous avons essayé de nous connecter. Python essaie donc de se connecter à une base de données s’exécutant localement. Cela ne fonctionnera pas car le service ne s’exécute pas localement, il s’exécute dans un autre conteneur, comme si c’était sur un autre ordinateur.

Vous pouvez accéder au conteneur lié en utilisant le nom que vous avez défini lors de la création du lien. Dans notre cas, nous utilisons + postgres + pour référencer le conteneur + postgres_box + qui exécute notre serveur de base de données. Vous pouvez le vérifier en consultant le fichier + / etc / hosts + dans le conteneur + python_box +:

cat /etc/hosts

Vous verrez tous les hôtes disponibles avec leurs noms et adresses IP. Notre serveur + postgres + est clairement visible.

Output127.0.0.1       localhost
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

172.17.0.3      3053f74c8c13

Modifions donc notre script Python et ajoutons le nom d’hôte. Ouvrez le fichier.

nano pg_test.py

Puis spécifiez l’hôte dans la chaîne de connexion:

/pg_test.py

"""Test PostgreSQL connection."""
import psycopg2

conn = psycopg2.connect( user='postgres')
print(conn)

Enregistrez le fichier, puis réexécutez le script.

python3 pg_test.py

Cette fois, le script se termine sans erreur:

Output<connection object at 0x7f64caec69d8; dsn: 'user=postgres host=7a230b56cd64', closed: 0>

N’oubliez pas les noms des conteneurs lorsque vous essayez de vous connecter à des services situés dans d’autres conteneurs et modifiez les informations d’identification de votre application pour référencer les noms liés de ces conteneurs.

Conclusion

Nous venons de décrire les problèmes les plus courants que vous pouvez rencontrer lors de l’utilisation de conteneurs Docker, de la création d’images au déploiement d’un réseau de conteneurs.

Docker a un drapeau + - debug + qui est principalement destiné aux développeurs Docker. Toutefois, si vous souhaitez en savoir plus sur les composants internes de Docker, essayez d’exécuter les commandes Docker en mode débogage pour obtenir une sortie plus détaillée:

docker -D [command] [arguments]

Bien que les conteneurs dans les logiciels existent depuis un certain temps, Docker n’existe que depuis trois ans et peut être assez complexe. Prenez votre temps pour vous familiariser avec les termes et l’écosystème, et vous verrez comment certains concepts qui étaient un peu étrangers au début bientôt beaucoup de sens.