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

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

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

Компоненты Docker

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

  1. Docker host (докер-хост)

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

  2. Docker daemon (докер-демон)

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

  3. Docker client (докер-клиент)

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

  4. Docker image (докер-образ)

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

  5. Docker container (докер-контейнер)

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

  6. Docker registry

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

  7. Dockerfile (докер-файл)

    Это файл-инструкция для сборки образа.

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

  • docker pull. Эта команда скачивает образ из docker registry и помещает его в локальное хранилище на хосте.
  • docker build. Эта команда читает докер-файл и собирает образ.
  • docker run. Эта команда создает из образа контейнер и запускает его.
Компоненты Docker и схема работы команд

Как устроены докер-образы и контейнеры

Докер-образ состоит из слоев. Каждая команда в докер-файле добавляет новый слой, который накладывается на предыдущий. Финальный докер-образ — это объединение всех слоев в один.

Такая структура позволяет использовать уже существующие образы для создания новых. Например, мы разрабатываем приложения на Python. Чтобы наши приложения запускались на других серверах, мы должны в каждый образ устанавливать среду выполнения Python. Чтобы не реализовывать это самостоятельно, мы просто используем готовый официальный образ Python. В свою очередь, этот образ основан на базе образа Debian — дистрибутива Linux. Без него Python не сможет работать.

В результате наш докер-образ упрощенно выглядит вот так:

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

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

Когда к Docker-образу добавляется слой для записи, получается Docker-контейнер

Создание и запуск докер-контейнеров

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

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

Запускаем контейнер

Сначала мы скачаем уже собранный образ и запустим контейнер. Выполним в консоли команду:

docker run debian echo 'This is Debian'

Этой командой мы говорим докеру: «Запусти образ debian и выполни в нем вот эту команду». В результате мы должны получить одну строчку с текстом This is Debian.

Смотрим результат в консоли:

Unable to find image 'debian:latest' locally
latest: Pulling from library/debian
d960726af2be: Pull complete  
Digest: sha256:acf7795dc91df17e10effee064bd229580a9c34213b4dba578d64768af5d8c51
Status: Downloaded newer image for debian:latest
This is Debian

Поясним, что тут происходит. Так как у нас свежая установка докера, у нас еще нет ни одного скачанного образа. Поэтому прежде чем использовать образ debian, его нужно откуда-то взять. По умолчанию Docker настроен на публичный репозиторий Docker Hub. Поэтому когда докер не нашел на нашем компьютере запрашиваемый образ, он решил найти его на докер-хабе и скачать. Docker самостоятельно определил, что ему сначала нужно выполнить команду docker pull и лишь затем docker run. И только после этого мы увидели результат в консоли. Если мы запустим эту же команду повторно, докер уже не будет скачивать образ, и мы сразу получим результат.

Создаем собственный образ

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

mkdir my-docker-app; cd my-docker-app

Далее создадим текстовый файл app.py и в любом текстовом редакторе запишем в него одну строчку кода:

print("This is Python");

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

python app.py

В консоли мы увидим результат: This is Python. Это все, что делает наше приложение: выводит одну строку текста. Но сейчас это не важно, ведь главная задача для нас — научиться упаковывать приложения в докер-образ.

Далее в этой же директории создадим файл с названием Dockerfile и запишем в него следующее:

FROM python:3
COPY app.py /
CMD [ "python3",  "./app.py" ]

Поясним, что мы сделали:

  1. В первой строке мы указываем, что используем базовый образ Python, версию 3.
  2. Далее копируем файл нашего приложения в корневую директорию образа.
  3. Третьей командой мы запускаем наш файл.

Но пока это еще не докер-образ, а просто файл с командами. Чтобы из докер-файла собрать образ, запустим команду:

docker build -t my-docker-app .

Параметр -t обозначает имя создаваемого образа, мы назовем его my-docker-app.

После того как образ собран, мы можем запустить контейнер:

docker run my-docker-app

В результате в консоли будет результат: This is Python. Итак, мы создали простое Python-приложение, обернули его в докер-образ и запустили контейнер. Конечно, это очень простой пример, и в настоящих приложениях все намного сложнее. Но это позволяет понять основы технологии и как с ней работать.

Аналоги Docker

Docker стал стандартом де-факто в мире контейнеров. Часто, когда говорят про контейнеры, подразумевают именно Docker-контейнеры. Но это не единственная реализация технологии контейнеризации, есть и другие:

  1. Имеет интерфейс командной строки, который очень похож на команды Docker. Основное отличие от докера заключается в том, что у Podman нет отдельного демона, это самостоятельная утилита. Podman используется как инструмент управления контейнерами по умолчанию в дистрибутиве Fedora Linux.

  2. Система виртуализации на уровне ОС, а не самостоятельная платформа, как Docker. Она создает отдельное виртуальное окружение с собственным пространством процессов и сетевым стеком, в котором все контейнеры используют один экземпляр ядра ОС. LXC часто рассматривается как среднее между chroot и полноценной виртуальной машиной.

  3. Движок, который изначально был ориентирован на современные облачные приложения. В 2019 году разработка прекратилась, и движок переведен в архив. Возможно, еще можно встретить проекты, где он используется. Но в новых разработках rkt уже не стоит выбирать.

Docker подходит для облаков

Докер-контейнеры можно запускать не только на своих серверах, но и в облаке. Во-первых, это позволит не заниматься выбором, покупкой и настройкой серверов. Во-вторых, у облачных провайдеров разработано много готовых сервисов, которые позволяют получить дополнительные преимущества. Например, Kubernetes на платформе Mail.ru Cloud Solutions управляет жизненным циклом контейнеров, автоматически масштабируется под изменение нагрузки и помогает построить отказоустойчивую систему.