Самые распространённые ошибки при работе с Docker

Docker позволяет заключать приложения и сервисы в контейнеры. Однако во время создания образа или объединения слоев иногда возникают ошибки. Причиной ошибки может быть опечатка, проблемы библиотек поддержки, конфликт имен, сбой взаимодействия между контейнерами.

Данное руководство по устранению неполадок предназначено для новичков Docker. Руководство поможет вам устранить проблемы при сборке образов и настройке соединений между контейнерами.

Требования

Для выполнения руководства нужно предварительно установить приложение Docker. Инструкции по установке можно найти по ссылкам:

Также вы можете посетить сайт Docker или следовать официальной документации по установке.

1: Ошибки в Dockerfile

Наибольшее количество ошибок обычно сосредоточено в Dockerfile.

Прежде чем приступить к исправлению ошибок, нужно понять разницу между образом и контейнером.

Образ Docker – это ресурс, предназначенный только для чтения, который создаётся с помощью файла под названием Dockerfile. Образы можно выкладывать на Docker Hub.

Контейнер – это экземпляр приложения, который создаётся с помощью образа.

Читайте также: Экосистема Docker: основы контейнеризации

Dockerfile подробно описывает пошаговый процесс, с помощью которого Docker создаёт образ. Каждая строка в Dockerfile описывает один из этапов процесса. Таким образом, если Docker переходит на новый этап, значит, предыдущие этапы выполнены без ошибок.

Создайте небольшой проект, чтобы на его примере рассмотреть несколько общих ошибок Dockerfile. Создайте каталог docker_image в домашнем каталоге. Затем создайте в нём Dockerfile.

mkdir ~/docker_image
nano ~/docker_image/Dockerfile

Добавьте в файл такие строки:

# base image
FROM debian:latest
# install basic apps
RUN aapt-get install -qy nano

В этом файле специально сделана опечатка. Можете её найти? Попробуйте создать образ с помощью этого файла и посмотрите, что скажет Docker.

docker build -t my_image ~/docker_image

Команда вернёт:

Step 2 : RUN aapt-get install -qy nano
---> Running in 085fa10ffcc2
/bin/sh: 1: aapt-get: not found
The command '/bin/sh -c aapt-get install -qy nano' returned a non-zero code: 127

Из этого сообщения об ошибке следует, что во второй команде допущена ошибка. Как уже говорилось, это сделано намеренно: вместо команды apt-get в файле указана команда aapt-get. Первый этап выполнен без ошибок, а на втором Docker обнаруживает опечатку.

Исправьте опечатку в Dockerfile:

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

Снова запустите docker build:

docker build -t my_image ~/docker_image

Команда вернёт:

Sending 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...
E: Unable to locate package nano
The command '/bin/sh -c apt-get install -qy nano' returned a non-zero code: 100

Теперь процесс проходил немного быстрее: Docker кэширует удачно выполненные этапы, чтобы потом не перевыполнять их. Однако потом возникла новая ошибка.

Дистрибутив Debian, на котором основан образ, не может найти текстовый редактор nano, хотя он точно доступен в репозитории Debian. Базовый образ собирается из кэшированных метаданных: репозиториев и списков доступных пакетов. Вероятно, при кэшировании произошла ошибка.

Чтобы исправить её, отредактируйте Dockerfile и добавьте в него чистку и обновление исходных файлов перед установкой новых пакетов:

nano ~/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

Сохраните и закройте файл. Запустите docker build:

docker build -t my_image ~/docker_image

Теперь процесс будет выполнен успешно:

Sending 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

Добавьте в образ Python 3 и базу данных PostgreSQL. Откройте Dockerfile:

nano ~/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
RUN apt-get install -qy python3
RUN apt-get install -qy python3-psycopg2

Сохраните и закройте файл. Соберите образ.

docker build -t my_image ~/docker_image

Как видите, все пакеты установлены без ошибок. Кроме того, процесс выполняется очень быстро, поскольку большинство его этапов помещено в кэш.

Sending 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
Removing intermediate container d7d4376c9f0d
Successfully built 978e0fa7afa7

Примечание: Docker кэширует процесс сборки. Потому иногда при сборке образа используются устаревшие исходные файлы. Чтобы избежать этого, добавьте в Dockerfile чистку и обновление исходников. Если при установке или обновлении пакетов возникает ошибка, запустите внутри контейнера команду:

apt-get clean && apt-get update

Внимательно читайте вывод Docker, чтобы отследить опечатки. Своевременно обновляйте исходные файлы, чтобы избежать ошибок, вызванных кэшированными списками пакетов.

Синтаксические ошибки и ошибки кэширования – наиболее распространенные проблемы, с которыми вы можете столкнуться при создании образа Docker. Теперь давайте рассмотрим проблемы, которые могут возникнуть при работе с контейнерами, созданными на основе этих образов.

2: Конфликты имён

Чем больше вы запускаете контейнеров, тем выше вероятность возникновения конфликта имён.

К примеру, иногда при создании контейнера пользователи пытаются использовать имя, которое ранее было присвоено другому контейнеру в этой системе. Данный раздел научит вас выбирать имена для контейнеров, переименовывать и удалять их, чтобы устранить конфликт имён.

Используйте созданный ранее образ, чтобы запустить контейнер. Затем, чтобы протестировать его работу, запустите интерактивный интерпретатор bash внутри этого контейнера.

docker run -ti my_image bash

После запуска контейнера вы увидите командную строку root:

root@80a0ca58d6ec: / #

Теперь давайте рассмотрим проблемы, которые могут возникнуть в контейнере.

Запуская контейнер так, как показано выше, не указывая имя, Docker присваивает ему случайное имя. Чтобы просмотреть список запущенных контейнеров, запустите на хосте Docker команду:

docker ps

Примечание: Команду нужно запускать вне контейнера.

Откройте терминал хоста Docker и запустите:

docker ps

Эта команда выведет список запущенных контейнеров:

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

Как видите, Docker выбрал для только что запущенного контейнера случайное имя, и в данном случае это loving_brahmagupta (вероятно, в вашем случае имя будет отличаться). Позволять Docker присваивать контейнерам случайные имена можно в некоторых простых случаях. Однако это может повлечь серьёзные проблемы. При развёртывании объемного проекта нужно присвоить контейнерам имена самостоятельно, чтоб иметь возможность ссылаться на них и автоматизировать их работу.

Чтобы задать имя контейнера, используйте при запуске аргумент –name. Также вы можете переименовать запущенный контейнер. Имя контейнера должно быть описательным.

Запустите следующую команду в терминале хоста Docker:

docker rename your_container_name python_box

Запросите список контейнеров:

docker ps

Теперь контейнер loving_brahmagupta называется python_box:

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

Чтобы закрыть контейнер, введите exit в командную строку:

root@80a0ca58d6ec: / # exit

Также контейнер можно остановить с помощью команды kill из другого терминала хоста Docker:

docker kill python_box

В таком случае Docker возвращает имя остановленного контейнера:

python_box

Чтобы убедиться, что контейнер python_box остановлен, запросите список контейнеров:

docker ps

Контейнер python_box в списке нет:

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

Вы думаете, что теперь можно запустить другой контейнер с именем python_box? Что ж, попробуйте сделать это:

docker run --name python_box -ti my_image bash
docker: 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'.

Если вы собираете образ, а затем хотите повторно использовать имя существующего образа, этот образ будет переписан. Контейнеры работают немного сложнее: вы не можете переписать существующий контейнер.

Итак, Docker говорит, что контейнер python_box уже существует, хотя только что этот контейнер был остановлен. Его даже нет в списке команды docker ps. Да, на данный момент он не запущен, но он всё ещё доступен. Вы остановили, но не удалили его. Команда docker ps показывает не все доступные, а только запущенные контейнеры.

Чтобы запросить список всех контейнеров, нужно добавить флаг –а:

docker ps -a

Как видите, python_box всё-таки в списке:

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

Контейнер существует, его состояние – Exited (137). Потому пока что вы не можете создать контейнер с таким же именем: для этого нужно удалить контейнер.

Чтобы сделать это, введите в терминал:

docker rm python_box

Docker выведет на экран имя удалённого контейнера.

python_box

Примечание: Если контейнер, который нужно удалить, всё ещё запущен, команда не сможет удалить его и вернёт ошибку.

Теперь попробуйте снова создать контейнер по имени python_box:

docker run --name python_box -ti my_image bash

Процесс будет успешно выполнен, а на экране появится оболочка root:

root@c05ac9d0c010: / #

Теперь остановите и удалите этот контейнер, чтобы избежать ошибок при дальнейшей работе. Откройте новый терминал на хосте Docker и выполните команду:

docker kill python_box && docker rm python_box

Эта команда состоит из двух команд, потому Docker выведет имя контейнера дважды:

python_box
python_box

При возникновении конфликтов имён используйте команду docker ps -a

Самостоятельно присваивая имена контейнерам, вы можете легко управлять инфраструктурой. Кроме того, так гораздо проще устанавливать взаимодействие между контейнерами.

3: Проблемы взаимодействия контейнеров

Докер может легко обрабатывать несколько контейнеров, благодаря чему вы можете запускать различные сервисы и даже дублировать сервисы в контейнерах. Если с одним из сервисов случился сбой или его взломали, вы можете просто заменить его другим, сохраняя при этом остальную часть инфраструктуры нетронутой. Но вы можете столкнуться с проблемами при настройке взаимодействия между этими контейнерами.

Создайте два взаимодействующих между собой контейнера: пусть первый будет контейнером Python на основе уже существующего образа, а второй будет запускать PostgreSQL.

Примечание: Официальный образ для контейнера PostgreSQL можно найти на Docker Hub.

Сначала создайте контейнер PostgreSQL. Присвойте ему имя с помощью аргумента –name (например, postgres_box).

Прежде контейнеры запускались на переднем плане, занимая терминал. Теперь нужно запустить контейнер PostgreSQL в фоновом режиме. Для этого используется флаг –detach.

Вместо команды bash запустите в контейнере команду postgres, которая запустит сервер баз данных PostgreSQL.

docker run --name postgres_box --detach postgres

Docker загрузит образ с Docker Hub и создаст контейнер. Затем он вернёт полный ID контейнера, запущенного в фоновом режиме:

Unable 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
f6609b9e96cc874be0852e400381db76a19ebfa4bd94fe326477b70b8f0aff65

Просмотрите список запущенных контейнеров:

docker ps

В списке вы увидите запущенный в фоновом режиме контейнер postgres_box, который использует порт 5432 (стандартный порт PostgreSQL)

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

Теперь запустите контейнер Python. Чтобы программы внутри контейнера Python могли видеть сервисы внутри контейнера postgres_box, нужно вручную связать эти контейнеры с помощью флага –link.

Чтобы создать ссылку, нужно указать имя контейнера, а затем, после флага –link, задать имена контейнеров, которые нужно связать. В данном случае команда будет выглядеть так:

docker run --name python_box --link postgres_box:postgres -ti my_image bash

Попробуйте подключиться к PostgreSQL из контейнера python_box.

Ранее вы установили nano в python_box. Используйте этот текстовый редактор, чтобы создать простой сценарий Python и подключиться к PostgreSQL. В терминал контейнера python_box введите:

root@3053f74c8c13: / # nano pg_test.py

Добавьте в файл:

"""Test PostgreSQL connection."""
import psycopg2
conn = psycopg2.connect(user='postgres')
print(conn)

Сохраните и закройте файл. Попробуйте подключиться к БД с помощью этого сценария:

root@3053f74c8c13: / # python3 pg_test.py

В выводе говорится о том, что во время подключения произошла ошибка:

Traceback (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: could not connect to server: No such file or directory
Is the server running locally and accepting
connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"?

Итак, контейнер postgres_box запущен и связан с python_box. В чём же проблема? Дело в том, что во время соединения не был указан хост БД, потому Python пытается подключиться к ней локально. Но это не сработает, так как сервис запущен не локально – он работает в другом контейнере, а это то же самое, что на другом компьютере.

Вы можете получить доступ к связанным контейнерам, указав имя, использованное в ссылке. В данном случае мы используем postgres для ссылки на контейнер postgres_box, который запускает сервер базы данных. Вы можете убедиться в этом, просмотрев файл /etc/hosts внутри контейнера python_box:

root@3053f74c8c13: / # cat /etc/hosts

Вы увидите все доступные хосты, их имена и IP. Сервер postgres тоже в списке:

127.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.2      postgres f6609b9e96cc postgres_box
172.17.0.3      3053f74c8c13

Отредактируйте сценарий Python и добавьте в него имя хоста. Откройте файл:

root@3053f74c8c13: / # nano pg_test.py

Укажите имя хоста:

"""Test PostgreSQL connection."""
import psycopg2
conn = psycopg2.connect(host='postgres', user='postgres')
print(conn)

Сохраните и закройте файл. Снова запустите сценарий:

root@3053f74c8c13: / # python3 pg_test.py

Теперь сценарий выполнен успешно:

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

Запоминайте имена контейнеров, чтобы иметь возможность подключаться к сервисам внутри этих контейнеров.

Заключение

В данном руководстве мы рассмотрели наиболее распространенные проблемы, с которыми можно столкнуться при работе с контейнерами Docker: от создания образов и до развертывания сетей контейнеров.

Docker предоставляет флаг –debug. В основном этим флагом пользуются разработчики Docker. Однако если вы хотите узнать больше о внутреннем строении Docker, попробуйте запустить команды Docker в режиме отладки (это вернёт более подробный вывод):

docker -D [command] [arguments]

Контейнеры программного обеспечения существуют уже некоторое время, а сама система Docker – всего три года, потому иногда с ней бывает сложно. Чем чаще вы будете работать с Docker, тем больше у вас будет опыта: вы будете знать подводные камни, научитесь быстро справляться с рутинными задачами и устранять ошибки.

Читайте также:

Tags: ,

Добавить комментарий