Что такое Docker: для чего он нужен и где используется
T-Rex
Тираннозавр Рекс

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

Managed Kubernetes помогает разворачивать контейнерные приложения в инфраструктуре Selectel. Сосредоточьтесь на разработке, а мы займемся рутинными операциями по обеспечению работы вашего кластера Kubernetes.

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

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

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

Что такое контейнеры

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

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

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

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

Преимущества использования контейнеров Docker

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

Docker решает проблемы зависимостей и рабочего окружения

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

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

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

Изоляция и безопасность

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

Ускорение и автоматизация развертывания приложений и масштабируемость

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

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

Контейнеры приближают к микросервисной архитектуре

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

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

Docker compose — одновременно развернуть несколько контейнеров

Docker-compose позволяет разворачивать и настраивать несколько контейнеров одновременно. Например, для веб-приложения нужно развернуть стек LAMP: Linux, Apache, MySQL, PHP. Каждое из приложений — это отдельный контейнер. Но в этой ситуации нам нужны именно все контейнеры вместе, а не отдельно взятое приложение. Docker-compose позволяет развернуть и настроить все приложения одной командой, а без него пришлось разворачивать и настраивать каждый контейнер отдельно.

Хранение данных в Docker

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

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

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

Тома (Docker volumes)

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

Монтирование каталога (bind mount)

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

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

Архитектура (компоненты) Docker

Теперь расскажем подробнее про компоненты, из которых состоит Docker.

Docker daemon

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

Docker client (клиент)

Это интерфейс командной строки для управления docker daemon. Мы пользуемся этим клиентом, когда создаем и разворачиваем контейнеры, а клиент отправляет эти запросы в docker daemon.

Docker image (образ)

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

Приведем аналогию на примере установки операционной системы. В дистрибутиве (образе) ОС есть все, что необходимо для ее установки. Но этот образ нельзя запустить, для начала его нужно «развернуть» в готовую ОС. Так вот, дистрибутив для установки ОС — это Docker image, а установленная и работающая ОС — это Docker container. Но контейнеры обычно разворачиваются одной командой — это намного проще и быстрее, чем установка ОС.

Docker container (контейнер)

Это уже развернутое из образа и работающее приложение.

Docker Registry

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

Dockerfile

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

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

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

Первая строчка означает, что за основу мы берем образ с названием python версии 3 это называется базовый образ. Docker найдет его в docker registry, скачает и будет использовать за основу. Вторая строчка означает, что нужно скопировать файл main.py в корень файловой системы контейнера. Третья строчка означает, что нужно запустить python и передать ему в качестве параметра файл main.py.

Далее рассмотрим примеры нескольких команд докер и что происходит, когда мы их выполняем.

Все эти команды выполняются в Docker client, который отправляет их в docker daemon:

  • Команда docker build (зеленая стрелка) читает dockerfile и собирает образ.
  • Команда docker pull (красная стрелка) скачивает образ из docker registry. По умолчанию docker скачивает образы из публичного репозитория Docker Hub. Но можно создать свой репозиторий и настроить докер, чтобы он работал с ним.
  • Команда docker run (черная стрелка) берет образ и разворачивает его в контейнер.

Создаем виртуальную машину для работы с докер

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

В панели управления заходим в раздел «Облачная платформа» — «Серверы», нажимаем кнопку «Создать сервер».

На следующем экране выбираем параметры сервера: имя, регион, ОС, параметры производительности и так далее. Сейчас для нас важны параметры «Источник» — выбираем ОС Ubuntu 20.04 и «Конфигурация» — выбираем 2 vCPU и 8 ГБ оперативной памяти.

Далее обратите внимание на разделы «Сеть» и «Доступ». В разделе «Сеть» нужно выбрать подсеть с публичным адресом, чтобы к виртуальной машине можно было подключаться из интернета. В разделе «Доступ» будет указан пароль для root-пользователя, а также необходимо загрузить SSH-ключ, чтобы подключаться к виртуальной машине. Подробную инструкцию о подключении смотрите в Базе знаний.

После этого внизу страницы нажимаем кнопку «Создать». Виртуальная машина создается за несколько минут, и после того, как она перейдет в статус ACTIVE, к ней можно подключаться по SSH.

Установка Docker

Мы рассмотрим установку докера на примере Ubuntu. Если у вас другой дистрибутив Linux или операционная система — ищите соответствующую инструкцию на официальном сайте.

Для начала синхронизируем пакетную базу apt и установим нужные зависимости:

sudo apt-get update
sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg \
    lsb-release

Далее импортируем GPG-ключ для репозитория docker:

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

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

echo \
  "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Теперь можно устанавливать докер:

sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io

По умолчанию, доступ к docker daemon есть только у пользователя root. Чтобы с докером могли работать и другие пользователи, их нужно добавить в специальную группу — docker. Выполните эту команду из под обычного пользователя:

sudo usermod -aG docker $USER

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

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

Теперь попробуем запустить какое-нибудь готовое приложение. Выполните команду:

docker run ubuntu echo 'hello from ubuntu'

Команда docker run создает и запускает контейнер из образа. В этом примере мы создаем контейнер из образа ubuntu, затем выполняем в нем команду echo ‘hello from ubuntu’. Но так как у нас чистая установка докера и мы не скачали ни одного образа, докер сначала найдет этот образ в публичном репозитории Docker Hub, скачает, а потом создаст из него контейнер. В следующий раз, когда нам понадобится образ ubuntu, докер уже не будет его скачивать.

После выполнения команды в терминале появится строка hello from ubuntu, и контейнер сразу остановится. Теперь выполним другую команду:

docker run -it ubuntu

Эта команда запустит контейнер в интерактивном режиме, то есть контейнер запустится и будет ждать дальнейших команд. При этом мы окажемся внутри операционной системы контейнера: запустится оболочка (bash), и мы сможем выполнять какие-то команды внутри контейнера. Чтобы выйти из контейнера, введите команду exit.

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

Теперь создадим HelloWorld-приложение на Python, обернем его в образ и запустим.

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

mkdir first-docker-app
cd first-docker-app

Создадим файл main.py и запишем в него одну строчку кода:

echo 'print("Hello from python");' >> main.py

Проверим, что наша программа работает. Для этого выполним команду:

python main.py

В консоли должно выйти сообщение Hello from python. Это и есть наше простое приложение. Теперь нужно обернуть его в докер-образ. Для этого создадим файл Dockerfile и напишем в нем три строчки:

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

В первой строке мы указываем образ, который берем за основу. Так мы пишем приложение на Python, нужно чтобы в нашем образе он уже был установлен. Самый простой способ это сделать — использовать готовый официальный образ с Docker Hub. Цифра 3 — это тег. Он означает, что нужно использовать третью версию Python. Вместо этого можно было бы использовать тег latest, который означает самую последнюю версию, или можно было указать номер конкретной версии, например 3.8.8.

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

Третья строчка — запускаем python и передаем ему в качестве параметра имя нашего файла.

Теперь из этого докер-файла можно собирать образ. Выполним команду:

docker build -t first-docker-app .

Параметр -t обозначает имя нашего образа, мы назвали его first-docker-app.

Так как у нас еще нет скачанного образа python, то докер сам скачает его из Docker Hub и затем будет использовать его в качестве основы для создания нашего образа.

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

docker images

Мы увидим, что у нас установлено три образа:

REPOSITORY         TAG       IMAGE ID       CREATED         SIZE
first-docker-app   latest    649cceb4dfd2   4 seconds ago   885MB
python             3         b1aa63f57d3c   2 days ago      885MB
ubuntu             latest    8e428cff54c8   4 days ago      72.9MB

first-docker-app — это наш образ, который мы только что создали. python — это образ python, который докер автоматически скачал чтобы собрать наш образ. ubuntu — образ, который мы пробовали для запуска готового приложения.

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

docker run first-docker-app

В результате нам выведется результат: Hello from python.

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

Список полезных команд

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

Посмотреть список всех контейнеров

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

docker ps

Но по умолчанию выводятся только работающие контейнеры. Чтобы вывести все, в том числе и остановленные, используйте опцию -a:

docker ps -a

Остановить и удалить все докер контейнеры

Чтобы удалить контейнеры, сначала их нужно остановить. Первая команда остановит запущенные контейнеры, если они есть. А вторая команда — удалит их.

docker stop $(docker ps -a -q)
docker rm $(docker ps -a -q)

Запустить контейнер с последующим удалением

По умолчанию контейнеры после завершения работы останавливаются, но не удаляются. Они сохраняют свое состояние и при необходимости их можно запустить снова. Чтобы контейнер удалялся сразу после остановки, добавьте к команде docker run параметр —rm, например:

docker run --rm ubuntu echo 'hello from ubuntu'

Посмотреть список всех скачанных образов

docker images

Удалить докер образ

docker rmi <имя-образа>

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

unable to remove repository reference <имя-образа> (must force) - container <id-контейнера> is using its

Чтобы принудительно удалить образ, добавьте флаг -f:

docker rmi -f <имя-образа>

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

docker ps -a --filter ancestor=<название-образа>

Итог

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

Мы рассказали основы технологий, но не затронули более сложные темы, вроде Docker Swarm, настройку сети или настройки процессов CI/CD. Но этого вполне достаточно, чтобы погрузиться в основы технологий.

Что еще почитать по теме

Владимир Туров 18 сентября 2021

Разбираем редкого зверя от Nvidia — DGX A100

Крупные IT-компании располагают дорогими «игрушками», которые скрыты от взоров большинства пользователей. Сегодня мы приоткроем завесу тайны и расскажем про систему, которая оптимизирована для работы …
Владимир Туров 18 сентября 2021

Новое в блоге

bondar 24 сентября 2021

Piller CPM300: зачем мы устанавливаем новые динамические ИБП

Бесперебойная подача электропитания в серверные — одна из обязательств провайдера дата-центра перед клиентами. Но важна не только отказоустойчивость решения, но и его эффективность с точки потребления…
bondar 24 сентября 2021
Владимир Туров 18 сентября 2021

Разбираем редкого зверя от Nvidia — DGX A100

Крупные IT-компании располагают дорогими «игрушками», которые скрыты от взоров большинства пользователей. Сегодня мы приоткроем завесу тайны и расскажем про систему, которая оптимизирована для работы …
Владимир Туров 18 сентября 2021
Андрей Зайцев 8 сентября 2021

Продуктовый дайджест: новые серверы, Selectel Connect и обновления «Облачной платформы»

У нас обновления в конфигурациях серверов, кластерах Kubernetes и разделах базы знаний. А еще обратите внимание на сервис Selectel Connect. В конце дайджеста — анонс конференции по ML и записи митапов…
Андрей Зайцев 8 сентября 2021
Ульяна Малышева 12 августа 2021

RHVoice Lab: как серверы помогают создавать голоса для синтезаторов речи

В этом году Selectel стал поддерживать некоммерческий проект RHVoice Lab — лабораторию по созданию новых голосов для одноименного отечественного синтезатора речи. Его особенность в том, что синтезатор…
Ульяна Малышева 12 августа 2021