Как отлаживать и исправлять распространенные проблемы с докером

Вступление

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

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

Предпосылки

Для завершения этого урока вам понадобится

  • Docker установлен на сервере или вашем локальном компьютере.

Чтобы установить Docker на сервере, вы можете следовать инструкциям for CentOS 7 или for Ubuntu 16.04.

Вы можете посетить the веб-сайт Docker или следовать the официальная документация по установке, чтобы установить Docker на свой локальный компьютер. машина.

Шаг 1 - Решение проблем с Dockerfile

Чаще всего вы можете столкнуться с проблемами, когда создаете образ Docker из + Dockerfile. Прежде чем мы углубимся, давайте проясним разницу между изображениями и контейнерами.

  • Image - это ресурс только для чтения, который вы создаете с помощью файла конфигурации с именем + Dockerfile +. Это то, что вы отправляете и делитесь через http://dockerhub.com [Docker Hub] или ваш личный реестр.

  • Container - это экземпляр для чтения и записи, который вы создаете на основе созданного вами изображения.

Вы можете узнать больше об этих понятиях в учебнике Docker объяснил: Использование Dockerfiles для автоматизации сборки изображений.

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

Давайте создадим небольшой проект, чтобы изучить некоторые проблемы, с которыми вы можете столкнуться при использовании + Dockerfile. Создайте каталог + docker_image + в вашем домашнем каталоге и используйте + nano + или ваш любимый редактор, чтобы создать + Dockerfile + в этой папке.

mkdir ~/docker_image
nano ~/docker_image/Dockerfile

Добавьте следующее содержимое в этот новый файл:

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

Вы увидите это сообщение в своем терминале, указывающее на ошибку:

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

Сообщение об ошибке в конце означает, что возникла проблема с командой на шаге 2. В данном случае это была наша преднамеренная опечатка: у нас есть + aapt-get + вместо + apt-get +. Но это также означало, что предыдущий шаг выполнен правильно.

Измените + Dockerfile + и внесите исправление:

Dockerfile

# install basic apps
RUN  install -qy nano

Теперь снова запустите команду + docker build:

docker build -t my_image ~/docker_image

И теперь вы увидите следующий вывод:

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...

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

Дистрибутив Debian, который мы использовали в качестве основы для нашего образа, не смог найти текстовый редактор + nano +, хотя мы знаем, что он доступен в репозиториях пакетов Debian. Базовое изображение поставляется с кэшированными метаданными, такими как репозитории и списки доступных пакетов. Иногда вы можете столкнуться с некоторыми проблемами с кэшем, если изменились действующие репозитории, из которых вы извлекаете данные.

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

nano ~/docker_image/Dockerfile

Добавьте следующую выделенную строку в файл, above команда для установки + nano +:

~ / Docker_image / Dockerfile

# base image
FROM debian:latest

# clean and update sources


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

Сохраните файл и снова запустите команду + docker build:

docker build -t my_image ~/docker_image

На этот раз процесс завершается успешно.

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

Давайте посмотрим, что произойдет, когда мы добавим Python 3 и драйвер PostgreSQL в наш образ. Снова откройте + Dockerfile +.

nano ~/docker_image/Dockerfile

И добавьте два новых шага для установки Python 3 и драйвера 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

Сохраните файл, выйдите из редактора и снова создайте изображение:

docker build -t my_image ~/docker_image

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

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

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

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

Шаг 2 - Решение проблем с именами контейнеров

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

Давайте запустим контейнер из образа, который мы создали в предыдущем разделе. В этом контейнере мы запустим интерактивный интерпретатор bash, чтобы проверить все. Выполните следующую команду:

docker run -ti my_image bash

Когда контейнер запустится, вы увидите приглашение root в ожидании инструкций:

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

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

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

docker ps

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

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

Имя + loving_brahmagupta + в предыдущем выводе - это имя, которое Docker автоматически назначил контейнеру в предыдущем примере; у тебя будет другое имя. Позволить Docker назначить имя вашему контейнеру хорошо в очень простых случаях, но может представлять значительные проблемы; когда мы развертываем, нам нужно последовательно называть контейнеры, чтобы мы могли ссылаться на них и легко их автоматизировать.

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

Выполните следующую команду из терминала хоста Docker:

docker rename  python_box

Затем перечислите ваши контейнеры:

docker ps

В выводе вы увидите контейнер + python_box +, подтверждающий, что вы успешно переименовали контейнер:

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

Чтобы закрыть контейнер, введите + exit + в командной строке терминала, содержащего запущенный контейнер:

exit

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

docker kill python_box

Когда вы таким образом уничтожаете контейнер, Docker возвращает имя только что уничтоженного контейнера:

Outputpython_box

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

docker ps

Как и ожидалось, контейнера больше нет в списке:

OutputCONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

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

На этот раз мы будем использовать аргумент + - name + для установки имени контейнера:

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'.

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

Докер говорит, что + python_box + уже существует, хотя мы только что убили его, и он даже не указан в + docker ps +. Он не работает, но все еще доступен, если вы захотите запустить его снова. Мы остановили это, но мы не удалили это. Команда + docker ps + показывает только running контейнеры, но не all контейнеры.

Чтобы вывести список all контейнеров Docker, работающих и других, передайте флаг + -a + (псевдоним для + - all +) в + docker ps +:

docker ps -a

Теперь наш контейнер + python_box + появляется в выводе:

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

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

Если вы хотите полностью удалить контейнер, вы используете команду + docker rm +. Выполните эту команду в своем терминале:

docker rm python_box

Еще раз, Docker выводит имя контейнера, который был только что удален:

Outputpython_box

Давайте создадим новый контейнер с именем + python_box + теперь, когда мы удалили предыдущий:

docker run --name python_box -ti my_image bash

Процесс завершается, и мы снова получаем корневую оболочку:

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

docker kill python_box && docker rm python_box

Мы объединили две команды вместе, поэтому в выходных данных имя контейнера отображается дважды. Первый вывод подтверждает, что мы уничтожили контейнер, а другой подтверждает, что мы его удалили.

Outputpython_box
python_box

Помните + docker ps -a + при возникновении проблем с именами и убедитесь, что ваши контейнеры остановлены и удалены, прежде чем пытаться воссоздать их с тем же именем.

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

Шаг 3 - Решение проблем с контейнерной связью

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

Давайте создадим два контейнера, которые общаются, чтобы мы могли исследовать потенциальные проблемы общения. Мы создадим один контейнер с Python, используя наш существующий образ, а другой контейнер с экземпляром PostgreSQL. Для этого контейнера мы будем использовать официальный образ PostgreSQL, доступный на http://dockerhub.com [Docker Hub].

Давайте сначала создадим контейнер PostgreSQL. Мы дадим этому контейнеру имя, используя флаг + - name +, чтобы мы могли легко его идентифицировать при связывании с другими контейнерами. Мы назовем это + postgres_box +.

Ранее, когда мы запускали контейнер, он работал на переднем плане, захватывая наш терминал. Мы хотим запустить контейнер базы данных PostgreSQL в фоновом режиме, что мы можем сделать с флагом + - detach +.

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

Выполните следующую команду, чтобы запустить контейнер:

docker run --name postgres_box --detach postgres

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

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

Перечислите контейнеры, чтобы убедиться, что этот новый контейнер работает:

docker ps

Вывод подтверждает, что контейнер + postgres_box + работает в фоновом режиме, предоставляя порт + 5432 +, порт базы данных PostgreSQL:

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

Теперь давайте запустим контейнер Python. Чтобы программы, работающие внутри контейнера Python, «видели» службы в контейнере + postgres_box +, нам нужно вручную связать наш контейнер Python с контейнером + postgres_box + с помощью аргумента + - link +. Чтобы создать ссылку, мы указываем имя контейнера, за которым следует имя ссылки. Мы будем использовать имя ссылки для ссылки на контейнер + postgres_box + внутри контейнера Python.

Выполните следующую команду, чтобы запустить контейнер Python:

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

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

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

nano pg_test.py

Затем добавьте следующий скрипт Python в файл:

pg_test.py

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

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

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

python3 pg_test.py

Вывод, который мы видим, указывает на проблему с подключением к базе данных:

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:

Мы убедились, что контейнер + postgres_box + работает, и связали его с контейнером + python_box +, так что же тогда произошло? Ну, мы никогда не указывали хост базы данных, когда пытались подключиться, поэтому Python пытается подключиться к базе данных, работающей локально, и это не сработает, потому что служба не работает локально, она работает в другом контейнере, как если бы это было на другом компьютере.

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

cat /etc/hosts

Вы увидите все доступные хосты с их именами и IP-адресами. Наш сервер + postgres + хорошо виден.

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

Итак, давайте изменим наш скрипт на Python и добавим имя хоста. Откройте файл.

nano pg_test.py

Затем укажите хост в строке подключения:

/pg_test.py

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

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

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

python3 pg_test.py

На этот раз скрипт завершается без ошибок:

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

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

Заключение

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

Docker имеет флаг + - debug +, который предназначен в основном для разработчиков Docker. Однако, если вы хотите узнать больше о внутренностях Docker, попробуйте запустить команды Docker в режиме отладки для более подробного вывода:

docker -D [command] [arguments]

Хотя контейнеры в программном обеспечении существуют в течение некоторого времени, сам Docker существует только три года и может быть довольно сложным. Не торопитесь, чтобы ознакомиться с условиями и the экосистемой, и вы увидите, как некоторые концепции, которые на первый взгляд были немного чуждыми скоро иметь много смысла.

Related