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

Docker  —  платформа с открытым исходным кодом для создания, развертывания и управления контейнеризированными приложениями.

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

Содержание:

  1. Образы и контейнеры.
  2. Запуск первой программы в Docker.
  3. Основные команды контейнеров Docker.
  4. Командная строка Docker-контейнера.
  5. Пользовательские образы Docker.
  6. Запуск веб-приложения в Docker.
  7. Docker и сопоставление портов.
  8. Выводы.

Официальная документация по установке Docker для разных платформ: Linux, Windows, macOS.


1. Образы и контейнеры

Самые важные концепции Docker!

  • Что такое “образ” или “image”?

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

docker images
Образы Docker!
  • Что такое “контейнер”?

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

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

docker ps -a
Контейнеры Docker!

2. Запуск первой программы в Docker

Первым делом нужно вывести на экран фразу “Hello, world!”.

В Docker каждому контейнеру нужен образ, поэтому, чтобы запустить hello-world, введите в консоль или терминал команду:

docker run hello-world
  • Что делает docker run?

Попытка создания и запуска контейнера. Согласно документации API-клиента Docker, в установленном порядке выполняются следующие действия.

  1. Консоль: выполняет команду docker run hello-world.
  2. Docker client (CLI): получает команду и выполняет действия на ее основе.
  3. Сервер Docker: связывается с CLI, чтобы выдать клиенту из кэша образов (Image Cache) нужный образ с названием hello-world. Если такого образа нет, то сервер Docker связывается с хабом Docker, где скачивает образ с указанным именем.
  4. Docker Hub: хаб Docker получает запрос от сервера Docker на загрузку конкретного файла образа из общедоступного хранилища. 
  5. Сервер Docker: снова ищет образ в разделе “Image Cache”, затем создает контейнер  —  экземпляр образа hello-world
  6. Контейнер Docker: согласно первоначальным установкам из образа, в контейнере запускается простая программа hello-world
  7. Консоль: в результате успешного выполнения всех вышеизложенных этапов отобразит информацию со скриншота.
Ваше первое приложение в Docker!

3. Основные команды контейнеров Docker

Прочитайте перечень команд управления поведением контейнеров без изменения образов.

  • Удалить все неактивные контейнеры Docker:
docker system prune
  • Вывести на экран stderr и stdout от запущенных в контейнере программ, логирование контейнера Docker:
docker logs <container-id>
  • Индекс активных контейнеров:
docker ps
  • Индекс всех контейнеров, включая неактивные:
docker ps -a
  • Индекс образов, установленных в системе на данный момент:
docker images
  • Деактивировать Docker-контейнер по идентификатору:
docker stop <container-id>
  • Удалить неактивный контейнер по идентификатору:
docker rm <container-id>
  • Удалить конкретный образ по его названию:
docker rmi <image-name>

4. Командная строка Docker-контейнера

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

  • Как выполнить команду внутри контейнера Docker?
docker exec -it <container-id> <command>

Здесь exec позволяет выполнять команды в контейнере, а флаг -it разрешает прием ввода от пользователя к процессу, а также вывод в терминал как результата выполнения процесса, так и сообщений об ошибках.

  • Как получить доступ к командной строке контейнера Docker?

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

docker exec -it <container-id> sh

sh выдаст разрешение на ввод подсказок (prompt), и вы сможете выполнять внутри контейнера различные задачи, например:

Ввод с клавиатуры в контейнер Docker!
  • Как разрешить ввод при запуске контейнера из образа Docker?

Допустим, что вы хотите сразу после скачивания из хаба Docker запустить образ под названием busybox. Что вы сделаете?

docker run busybox

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

docker run -it busybox sh
Разрешение ввода при запуске образа!

Теперь все знания о полезных командах из двух пунктов руководства помогут вам подготовиться к следующему шагу.


5. Пользовательские образы Docker

Как добавить файлы в контейнер и какую операционную систему выбрать для образа?

  • Dockerfile

Основной файл, хранящий все инструкции. Каждый dockerfile одинаково структурирован. Чтобы создать dockerfile для пользовательского образа необходимо указать:

  1. Базовый образ.
  2. Все зависимости и условия запуска программы.
  3. Команду для запуска программы при старте контейнера.
  • Docker Client

После создания dockerfile вы сразу попытаетесь собрать образ. Следовательно, Dockerfile отправляется клиенту Docker.

  • Docker Server

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

  • Как же создать образ Docker?
  1. Сначала напишите dockerfile.
  2. Затем выполните команду docker build.
  3. Теперь запустите образ командой docker run <image-id>.

Рассмотрим простой dockerfile, в котором установлен сервер базы данных Redis:

Dockerfile для установки Redis-сервера!

Теперь, глядя на Dockerfile, у вас наверняка возникли вопросы! Ответим на них сейчас:

  • Что такое базовый образ? 

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

  • Почему Alpine?

Alpine Linux  —  это как Windows и macOS, где можно установить почти все, что вам нужно. Alpine занимает мало памяти, что хорошо подходит для установки Redis. Базовый образ загружается через инструкцию FROM.

  • Как установить зависимости?

Посмотрите, в dockerfile написана инструкция ‘RUN’, которая выполняет команды внутри контейнера. APK означает менеджер пакетов Alpine Linux. Команды apk применяются в любой работающей системе на базе Alpine Linux для удаления, установки, обновления программного обеспечения.

  • Как указать команду для запуска приложения в контейнере?

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

Сервер Redis в контейнере Docker

Теперь поговорим детальнее о команде docker build. Что происходит перед тем, как результат ее выполнения появляется на экране? 

  1. Загружается контейнер с образом alpine.
  2. Файловая система из образа alpine переносится во временный контейнер, в нем и выполняются инструкции по установке базы данных Redis. После завершения установки временный контейнер удаляется, а файловая система переносится обратно в образ alpine, тем самым обновляя его. Теперь в новом образе установлен Redis.
  3. По аналогии создается еще один временный контейнер для установки команды запуска сервера Redis при каждом запуске образа в контейнере. Следом временный контейнер удаляется, а обновленная файловая система устанавливается в конечном пользовательском образе.
  4. Наконец, для запуска контейнера по шаблону пользовательского образа выполняется команда docker run <container-id>.

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

  • Как установить специфическое имя для образа?

docker build -t <имя>/<репозиторий/имя проекта>:версия . 


6. Запуск веб-приложения в Docker

В этом разделе создадим и докеризируем простое приложение на Node.js на основе пользовательского образа. Не беспокойтесь, вам не нужны знания о Node.js для выполнения пунктов руководства.

  • Шаг 1
    Напишите файл package.json, ведь Express.js  —  это бэкенд-фреймворк для веб-приложений на Node.js. Укажите в нем последнюю версию при помощи символа звездочки *, а затем укажите запуск по умолчанию для команды node index.js, где index.js  —  это основной веб-файл.
package.json
  • Шаг 2
    Напишите простой файл index.json. Планируется, что фреймворк Express.js через / получает запросы и отправляет обратно ответ. Чтобы получить запрос и ответ, необходимо установить порт для прослушивания.
index.js
  • Шаг 3
    Напишите dockerfile. Помните все ключевые шаги? Если забыли, то поднимитесь вверх по разделам и освежите знания. Тем не менее совершенно очевидно, что для запуска приложения понадобятся установленные, готовые к работе Node.js и NPM.
Dockerfile
  • Шаг 4
    Постройте пользовательский образ, присвойте ему теги.
Запуск образа в контейнере
  • Шаг 5
    Запустите контейнер с приложением, запомните прослушиваемый порт.
Веб-приложение успешно запущено и прослушивает порт 8080
  • Шаг 5
    Проверьте, можно ли получить доступ к приложению из браузера, действительно ли оно работает?
    О, нет! Что-то пошло не так? Пришло время выяснить. 
Ошибка, приложение недоступно

Возможно, у вас сейчас на уме один вопрос: зачем в dockerfile написана команда WORKDIR /usr/app?

Основная цель  —  разделение рабочих файлов, чтобы строго необходимые для работы приложения файлы не смешивались с другими директориями, а легко отделялись от остальных при необходимости, если возникнут проблемы. Следовательно, внутри контейнера файлы веб-приложения сохраняются в каталоге /usr/app.

SSH терминал внутри контейнера

7. Docker и сопоставление портов

Рассмотрим, как разобраться в настройках и правильно установить сопоставление портов!

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

Сопоставление портов позволит запросу на порт 8080 с локальной машины перенаправить запрос на порт 8080 Docker-контейнера, только для входящих запросов. 

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

  • В команде запуска контейнера первый порт  —  это порт хост-машины или локальной машины, а второй порт  —  это порт контейнера:
docker run -p 8080:8080 mahedi/simpleweb
Верно настроенное сопоставление портов

Выводы

Веб-приложение запущено и доступно при обращении к порту 8080 локальной машины.

Приложение работает внутри контейнера Docker!

Несмотря на успех, стоит упомянуть три распространенные ошибки, допускаемые в процессе работы с Docker Server и Docker Client.

  1. Неправильный выбор базового образа.
  2. Отсутствие в dockerfile команд ADD или COPY для переноса файлов хост-машины в контейнер.
  3. Неправильное сопоставление портов при создании контейнера.

Далее прилагаются скриншоты правильно запущенного в Docker веб-приложения Node.js, чтобы вы могли с ними свериться.

Запуск приложения на Node.js в контейнере Docker через собственный dockerfile

Самостоятельно ознакомьтесь с файлом dockerfile для построения пользовательского образа и запуска веб-приложения на Node.js.

Запуск приложения на Node.js в контейнере Docker

Проанализируйте изображенный на скриншоте файл index.js с простым примером веб-приложения на Node.js. Данный файл запускается внутри контейнера Docker при помощи соответствующей команды из dockerfile.

На сегодня все! В следующей части руководства пойдет речь о последовательном создании нескольких контейнеров и докеризации сложных веб-систем со множеством зависимостей.

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

Читайте нас в Telegram, VK и Дзен


Перевод статьи Mahedi Hasan Jisan: Everything on Docker! (Part l)

Предыдущая статьяАрхитектура виртуальной машины Java: объяснение для начинающих
Следующая статьяПочему стоит использовать Pathlib в качестве альтернативы модуля OS