Docker зачем нужен: Docker. Зачем и как / Хабр
Docker. Зачем и как / Хабр
Есть множество прекрасных публикаций для тех, кто уже пользуется docker-ом. Есть хорошие статьи для тех, кто хочет этому научиться. Я пишу для тех, кто не только не знает, что такое docker, но и не уверен стоит ли ему это знать.
Я сознательно опускаю некоторые технические подробности, а кое где допускаю упрощения. Если вы увидите, что docker – то, что вам нужно, вы легко найдете более полную и точную информацию в других статьях.
Начну я с описания нескольких типичных проблем.
Проблемы
Первая проблема — как передать продукт клиенту.
Предположим у вас есть серверный проект, который вы закончили и теперь его необходимо передать пользователю. Вы готовите много разных файликов, скриптов и пишите инструкцию по установке. А потом тратите уйму времени на решения проблем клиента вроде: «у меня ничего не работает», «ваш скрипт упал на середине — что теперь делать», «я перепутал порядок шагов в инструкции и теперь не могу идти дальше» и т. п.
Всё усугубляется если продукт тиражируемый и вместо одного клиента у вас сотни или тысячи покупателей. И становится еще сложнее, если вспомнить о необходимости установки новых версий продукта.
Вторая проблема — тиражируемость. Пусть вам нужно поднять 5 (или 50) почти одинаковых серверов. Делать это вручную долго, дорого и подвержено ошибкам.
Наконец, третья проблема — переиспользуемость. Предположим у вас есть отдел, который делает браузерные игры. Предположим, что их у вас уже несколько. И все они используют один и тот же технологический стэк (например — java-tomcat-nginx-postgre). Но при этом, чтобы поставить новую игру вы вынуждены заново подготавливать на новом сервере почти одинаковую конфигурацию. Вы не можете просто так взять и сказать — «хочу сервер, как в игре странники но только с другим веб архивом»
Обычные решения
Как обычно решаются эти проблемы.
Установочный скрипт
Первый подход я уже упомянул — вы можете написать скрипт, который установит всё, что вам нужно и запускать его на всех нужных серверах. ( Скрипт может быть как простым sh файлом, так и чем-то сложным, созданным с использованием специальных инструментов).
Недостатки этого подхода — хрупкость и неустойчивость к ошибкам. Как бы хорошо не был написан скрипт, рано или поздно на какой-то машине он упадёт. И после этого падения машина фактически окажется «испорченной» — просто так «откатить» те действия, которые скрипт успел выполнить, у вашего клиента не получится.
Облачные сервисы
Второй подход — использование облачных сервисов. Вы вручную устанавливаете на виртуальный сервер всё, что вам нужно. Затем делаете его image. И далее клонируете его столько раз, сколько вам надо.
Недостатка здесь два. Во-первых, vendor-lock-in. Вы не можете запускать свое решение вне выбранного облака, что не всегда удобно и может привести к потерям несогласных с этим выбором клиентов. Во-вторых, облака медленны. Виртуальные (и даже «bare-metal») сервера предоставляемые облаками на сегодняшний день сильно уступают по производительности dedicated серверам.
Виртуальные машины
Третий подход — использование виртуальных машин. Здесь тоже есть недостатки:
Размер — не всегда удобно качать образ виртуальной машины, который может быть довольно большим. При этом, любое изменение внутри образа виртуальной машины требует скачать весь образ заново.
Сложное управление совместным использованием серверных ресурсов — не все виртуальные машины вообще поддерживают совместное использование памяти или CPU. Те что поддерживают, требуют тонкой настройки.
Подход докера — контейнеризация
И вот тут появляется docker, в котором
- есть контролируемая среда (как в виртуальных машинах)
- есть эффективное управление серверными ресурсами
- и нет vendor lock-in
Подобно виртуальной машине докер запускает свои процессы в собственной, заранее настроенной операционной системе. Но при этом все процессы докера работают на физическом host сервере деля все процессоры и всю доступную память со всеми другими процессами, запущенными в host системе. Подход, используемый докером находится посередине между запуском всего на физическом сервере и полной виртуализацией, предлагаемой виртуальными машинами. Этот подход называется контейнеризацией.
Как работает docker
Создание образа
Сначала создается docker image (или образ). Он создается при помощи скрипта, который вы для этого пишете.
Образы наследуются и, обычно, для создания своего первого образа мы берём готовый образ и наследуемся от него.
Чаще всего мы берем образ в котором содержится та или иная версия linux. Скрипт тогда начинается как-то так:
FROM ubuntu:16.04
Далее при помощи директивы RUN мы можем исполнять любые команды, которые поддерживает этот линукс.
Например RUN apt-get install -y mc
установит в наш образ midnight commander.
Кроме этого, мы можем копировать в наш образ любые локальные файлы при помощи директивы COPY.
Например:
COPY mzoo.war /opt/tomcat/webapps/ROOT.war
Докер поддерживает гораздо больше различных директив. Например, директива USER roman
говорит докеру что все следующие директивы нужно выполнять из под пользователя roman. А директива ENTRYPOINT [“/opt/tomcat/catalina.sh”]
задает исполняемый файл, который будет запускаться при старте.
Я не буду перечислять все остальные директивы — в этом нет смысла. Здесь главное — принцип: вы создаёте вот такой скрипт, называете его Dockerfile и запускаете команду docker build, docker выполняет скрипт и создает image.
Если в процессе возникают какие-то ошибки, докер о них сообщает и вы их исправляете. То есть исправление скрипта происходит на этапе создания image. На этапе установки скрипт уже не используется.
Создание контейнера
Когда у вас уже есть docker image вы можете создать из него контейнер на любом физическом сервере, где установлен докер. Если image – это тиражируемый образ некоторой «машины», то container это уже сама «машина», которую можно запускать и останавливать.
Важный момент — при создании контейнера из image, его можно параметризовать. Вы можете передавать докеру переменные окружения, которые он использует при создании контейнера из image. Так вы сможете создавать немного разные машины из одного образа. Например, передать образу web-сервера его доменное имя.
Хорошей практикой в докере считается «упаковка» в один контейнер ровно одного постоянно работающего серверного процесса. Как я уже упоминал, этот процесс работает на уровне физического сервера и честно регулируется установленной там операционной системой. Поэтому, в отличие от виртуальных машин, контейнеры докера не требуют специального управления памятью и процессорами. Использование ресурсов становится простым и эффективным.
Union filesystem
Ок — память и процессор используется эффективно. А как насчёт файловой системы? Ведь если у каждого контейнера докера своя собственная копия операционной системы, то мы получим ту же проблему, что и с виртуальными машинами — тяжеловесные образы, которые содержат одно и тоже.
На самом деле в докере это не так. Если вы используете 100500 контейнеров, основанных на одном и том же образе операционной системы, то файлы этой системы будут скачаны докером ровно один раз. Это достигается за счёт использования докером union file system.
Union file system состоит из слоёв (layers). Слои как бы наложены друг на друга. Некоторые слои защищены от записи. Например, все наши контейнеры используют общие защищенные от записи слои, в которых находятся неизменяемые файлы операционной системы.
Для изменяемых файлов каждый из контейнеров будет иметь собственный слой. Естественно, докер использует такой подход не только для операционной системы, но и для любых общих частей контейнеров, которые были созданы на основе общих «предков» их образов.
Container registry
Получается, что docker image состоит из слоёв. И хорошо было бы уметь скачивать на наш сервер только те слои, которых на нём пока нет. Иначе для установки 100 контейнеров, основанных на Ubuntu мы скачаем Ubuntu внутри их образов 100 раз. Зачем?
Хорошая новость в том, что докер решает эту проблему. Докер предоставляет специальный сервис, называемый docker registry. Docker registry предназначен для хранения и дистрибуции готовых образов. Собрав новый образ (или новую версию образа) вы можете закачать его в docker registry. Соответственно, потом его можно скачать оттуда на любой сервер. Главная фишка здесь в том, что физически качаться будут только те слои, которые нужны.
Например, если вы создали новую версию образа, в котором поменяли несколько файлов, то в registry будут отправлены только слои, содержащие эти файлы.
Аналогично, если сервер качает из registry какой-то образ, скачаны будут только слои, отсутствующие на сервере.
Docker registry существует и как общедоступный сервис и как open source проект, доступный для скачивания и установки на собственной инфрастуктуре.
Использование контейнеров
Созданные контейнеры можно запускать, останавливать, проверять их статус и т д. При создании контейнера можно дополнительно передать докеру некоторые параметры. Например, попросить докер автоматически рестартовать контейнер, если тот упадёт.
Взаимодействие между контейнерами
Если контейнеров на сервере несколько, управлять ими вручную становится проблематично. Для этого есть технология docker compose. Она существует поверх докера и просто позволяет управлять контейнерами на основе единого конфигурационного файла, в котором описаны контейнеры, их параметры и их взаимосвязи (например контейнер A имеет право соединяться с портом 5432 контейнера B)
Выводы
Таким образом докер очень хорошо подходит для решения перечисленных выше задач:
- удобная передача серверного проекта клиенту
- обеспечение тиражируемости серверов
- обеспечение переиспользуемости ранее созданных серверных конфигураций
Отдельно хочу отметить, что докер также крайне удобен для обновления ранее установленных версий продукта и для создания тестовых серверов, полностью идентичных «натуральным».
Что такое Docker и зачем он нужен — примеры использования 🐟
9 января 2020
6 176
Время чтения ≈ 12 минут
Содержание
Контейнеризация является отличной альтернативой аппаратной виртуализации. Все процессы в ней протекают на уровне операционной системы, что позволяет существенно экономить ресурсы и увеличивать эффективность работы с приложениями.
Одним из наиболее популярных инструментов для программной виртуализации является Docker — автоматизированное средство управления виртуальными контейнерами. Он решает множество задач, связанных с созданием контейнеров, размещением в них приложений, управлением процессами, а также тестированием ПО и его отдельных компонентов.
Что такое Docker и как его следует применять для веб-разработки, описано в этой статье.
Что такое Docker
Docker (Докер) — программное обеспечение с открытым исходным кодом, применяемое для разработки, тестирования, доставки и запуска веб-приложений в средах с поддержкой контейнеризации. Он нужен для более эффективного использование системы и ресурсов, быстрого развертывания готовых программных продуктов, а также для их масштабирования и переноса в другие среды с гарантированным сохранением стабильной работы.
Разработка Docker была начата в 2008 году, а в 2013 году он был опубликован как свободно распространяемое ПО под лицензией Apache 2.0. В качестве тестового приложения Docker был включен в дистрибутив Red Hat Enterprise Linux 6.5. В 2017 году была выпущена коммерческая версия Docker с расширенными возможностями.
Docker работает в Linux, ядро которых поддерживает cgroups, а также изоляцию пространства имен. Для инсталляции и использования на платформах, отличных от Linux, существуют специальные утилиты Kitematic или Docker Machine.
Основной принцип работы Docker — контейнеризация приложений. Этот тип виртуализации позволяет упаковывать программное обеспечение по изолированным средам — контейнерам. Каждый из этих виртуальных блоков содержит все нужные элементы для работы приложения. Это дает возможность одновременного запуска большого количества контейнеров на одном хосте.
Docker-контейнеры работают в разных средах: локальном центре обработки информации, облаке, персональных компьютерах и т. д.
Преимущества использования Docker
- Минимальное потребление ресурсов — контейнеры не виртуализируют всю операционную систему (ОС), а используют ядро хоста и изолируют программу на уровне процесса. Последний потребляет намного меньше ресурсов локального компьютера, чем виртуальная машина.
- Скоростное развертывание — вспомогательные компоненты можно не устанавливать, а использовать уже готовые docker-образы (шаблоны). Например, не имеет смысла постоянно устанавливать и настраивать Linux Ubuntu. Достаточно 1 раз ее инсталлировать, создать образ и постоянно использовать, лишь обновляя версию при необходимости.
- Удобное скрытие процессов — для каждого контейнера можно использовать разные методы обработки данных, скрывая фоновые процессы.
- Работа с небезопасным кодом — технология изоляции контейнеров позволяет запускать любой код без вреда для ОС.
- Простое масштабирование — любой проект можно расширить, внедрив новые контейнеры.
- Удобный запуск — приложение, находящееся внутри контейнера, можно запустить на любом docker-хосте.
- Оптимизация файловой системы — образ состоит из слоев, которые позволяют очень эффективно использовать файловую систему.
Компоненты Docker
Для начинающих разработчиков необходимо знать как работает Docker, его основные компоненты и связь между ними.
- Docker-демон (Docker-daemon) — сервер контейнеров, входящий в состав программных средств Docker. Демон управляет Docker-объектами (сети, хранилища, образы и контейнеры). Демон также может связываться с другими демонами для управления сервисами Docker.
- Docker-клиент (Docker-client / CLI) — интерфейс взаимодействия пользователя с Docker-демоном. Клиент и Демон — важнейшие компоненты «движка» Докера (Docker Engine). Клиент Docker может взаимодействовать с несколькими демонами.
- Docker-образ (Docker-image) — файл, включающий зависимости, сведения, конфигурацию для дальнейшего развертывания и инициализации контейнера.
- Docker-файл (Docker-file) — описание правил по сборке образа, в котором первая строка указывает на базовый образ. Последующие команды выполняют копирование файлов и установку программ для создания определенной среды для разработки.
- Docker-контейнер (Docker-container) — это легкий, автономный исполняемый пакет программного обеспечения, который включает в себя все необходимое для запуска приложения: код, среду выполнения, системные инструменты, системные библиотеки и настройки.
- Том (Volume) — эмуляция файловой системы для осуществления операций чтения и записи. Она создается автоматически с контейнером, поскольку некоторые приложения осуществляют сохранение данных.
- Реестр (Docker-registry) — зарезервированный сервер, используемый для хранения docker-образов. Примеры реестров:
- Центр Docker — реестр, используемый для загрузки docker-image. Он обеспечивает их размещение и интеграцию с GitHub и Bitbucket.
- Контейнеры Azure — предназначен для работы с образами и их компонентами в директории Azure (Azure Active Directory).
- Доверенный реестр Docker или DTR — служба docker-реестра для инсталляции на локальном компьютере или сети компании.
- Docker-хаб (Docker-hub) или хранилище данных — репозиторий, предназначенный для хранения образов с различным программным обеспечением. Наличие готовых элементов влияет на скорость разработки.
- Docker-хост (Docker-host) — машинная среда для запуска контейнеров с программным обеспечением.
- Docker-сети (Docker-networks) — применяются для организации сетевого интерфейса между приложениями, развернутыми в контейнерах.
Что такое Docker Engine
Docker Engine («Движок» Docker) — ядро механизма Докера. «Движок» отвечает за функционирование и обеспечение связи между основными Docker-объектами (реестром, образами и контейнерами).
Элементы Docker Engine
- Сервер выполняет инициализацию демона (фоновой программы), который применяется для управления и модификации контейнеров, образов и томов.
- REST API — механизм, отвечающий за организацию взаимодействия Докер-клиента и Докер-демона.
- Клиент — позволяет пользователю взаимодействовать с сервером при помощи команд, набираемых в интерфейсе (CLI).
Как работает Docker
Работа Docker основана на принципах клиент-серверной архитектуры, которая основана на взаимодействии клиента с веб-сервером (хостом). Первый отправляет запросы на получение данных, а второй их предоставляет.
Схема работы
- Пользователь отдает команду с помощью клиентского интерфейса Docker-демону, развернутому на Docker-хосте. Например, скачать готовый образ из реестра (хранилища Docker-образов) с помощью команды docker pull. Взаимодействие между клиентом и демоном обеспечивает REST API. Демон может использовать публичный (Docker Hub) или частный реестры.
- Исходя из команды, заданной клиентом, демон выполняет различные операции с образами на основе инструкций, прописанных в файле Dockerfile. Например, производит их автоматическую сборку с помощью команды docker build.
- Работа образа в контейнере. Например, запуск docker-image, посредством команды docker run или удаление контейнера через команду docker kill.
Как работают образы
Docker-image — шаблон только для чтения (read-only) с набором некоторых инструкций, предназначенных для создания контейнера. Он состоит из слоев, которые Docker комбинирует в один образ при помощи вспомогательной файловой системы UnionFS. Так решается проблема нерационального использования дисковой памяти. Параметры образа определяются в Docker-file.
Для многократного применения Docker-image следует пользоваться реестром образов или Докер-реестром (Docker-registry), позволяющим закачивать готовые образы с внешнего репозитория сервиса и хранить их в реестре Докер-хоста. Рекомендуемый вариант — официальный реестр компании Docker Trusted Registry (DTR).
Если требуется файл, то скачиваться будут только нужные слои. Например, разработчик решил доработать программное обеспечение и модифицировать образ, изменив несколько файлов. После загрузки на сервер будут отправлены слои, содержащие только модифицированные данные.
Как работают контейнеры
Каждый контейнер строится на основе Docker-образов. Контейнеры запускаются напрямую из ядра операционной системы Linux. Благодаря этому, они потребляют гораздо меньше ресурсов, чем при аппаратной виртуализации.
Изоляция рабочей среды осуществляется при помощи технологии namespace. Для каждого изолированного пространства (контейнера) создается уникальное пространство имен, которое и обеспечивает к нему доступ. Любой процесс, выполняемый внутри контейнера, ограничивается namespace.
В ОС Linux посредством Docker Engine используется немного другая технология — контрольные группы (cgroups). При этом приложение ограничивается некоторым набором ресурсов. Сgroups осуществляют обмен доступных аппаратных ресурсов с контейнерами, на которые дополнительно устанавливаются необходимые ограничения (использование памяти, прав доступа к другому ресурсу и т. д.).
Движок Docker объединяет пространство имен (namespace), контрольные группы (cgroups) и файловую систему (UnionFS) в формат контейнера. В будущем планируется поддержка других форматов посредством интеграции технологий BSD Jails или Solaris Zones.
Что происходит при запуске контейнера
- Происходит запуск образа (Docker-image). Docker Engine проверяет существование образа. Если образ уже существует локально, Docker использует его для нового контейнера. При его отсутствии выполняется скачивание с Docker Hub.
- Создание контейнера из образа.
- Разметка файловой системы и добавление слоя для записи.
- Создание сетевого интерфейса.
- Поиск и присвоение IP-адреса.
- Запуск указанного процесса.
- Захват ввода/вывода приложения.
Docker Compose
Для управления несколькими контейнерами, из которых состоит проект, используют пакетный менеджер — Docker Compose.
Он применяется не во всех случаях. Если проект является простым приложением, не требующим использования сторонних сервисов, то для его развертывания можно ограничиться только Docker. Docker Compose рекомендуется использовать при проектировании сложных программных продуктов, включающих в себя множество процессов и сервисов.
Docker Swarm
При преобразовании хостов в кластер нужно воспользоваться утилитой кластеризации Docker Swarm. Хост, находящийся в его составе, называется «узлом» (node), который бывает управляющим или рабочим. Один кластер содержит только один управляющий «узел».
Некоторые возможности утилиты
- Управление нагрузочными характеристиками — осуществляется оптимизация рассылки запросов между хостами, обеспечивая на них равномерную нагрузку.
- Динамическое управление — допускается добавление элементов в swarm-кластер без дальнейшего его перезапуска.
- Возможность масштабирования — позволяет добавлять или удалять docker-образ для автоматического создания контейнера.
- Восстановление «узла» после сбоя — работоспособность каждого хоста постоянно контролируется управляющим «узлом». При сбое кластера происходит его восстановление и перезапуск.
- Rolling-update — выполняет обновление контейнеров. Процедура может выполняться в определенной последовательности и с временной задержкой для запуска другого контейнера. Параметр указывается в настройках. Если произойдет сбой обновления, то Docker Swarm выдаст ошибку и процесс повторится заново.
Примеры применения
Окружение для разработки Docker применяется во множестве сфер — от обработки больших массивов данных, до работы с микросервисами, основанных на распределенной архитектуре.
Чтобы понять, как можно применять Докер на практике, разберем основные примеры использования для чайников.
- Быстрая доставка приложений (команды docker pull и docker push) позволяет организовать коллективную работу над проектом. Разработчики могут работать удаленно на локальных компьютерах и выполнять пересылку фрагментов кода в контейнер для тестов.
- Развертывание и масштабирование — контейнеры работоспособны на локальных компьютерах, серверах, в облачных онлайн-сервисах. Их можно загружать на хостинг для дальнейшего тестирования, создавать (docker run), останавливать (docker stop), запускать (docker start), приостанавливать и возобновлять (docker pause и docker unpause соответственно).
- Множественные нагрузки — осуществление запуска большого количества контейнеров на одном и том же оборудовании, поскольку Docker занимает небольшой объем дисковой памяти.
- Диспетчер процессов — возможность мониторинга процессов в Docker посредством команд docker ps и docker top, имеющими схожий синтаксис с Linux.
- Удобный поиск — в реестрах Docker он осуществляется очень просто. Для этого следует использовать команду docker search.
Заключение
Docker является важным инструментом для каждого современного разработчика, как основа аппаратной виртуализации приложений. Эта технология обладает широким функционалом и возможностями для контроля процессов. Докер позволяет не только развертывать контейнеры, но и оперативно масштабировать их экземпляры, работать с многоконтейнерными приложениями (Docker Compose), а также объединять несколько Докер-хостов в единый кластер (Docker Swarm).
Докер характеризуется достаточно простым синтаксисом. Поэтому он довольно прост в освоении как для опытных IT-специалистов, так и для новичков. Программное обеспечение совместимо со всеми версиями операционных систем Linux и Windows, поэтому область применения Docker практически не ограничена.
Чтобы использовать возможности контейнеризации на 100%, Docker нужна подходящая рабочая среда. Правильное решение — VPS от Eternalhost. Мощные виртуальные сервера с возможностью быстрого масштабирования ресурсов!
Оцените материал:
[Всего голосов: 2 Средний: 5/5]
Как и для чего использовать Docker – Hexlet Guides
Содержание
- Установка
- Запуск
- Зачем все это?
- Приложение в контейнере
- Работа с образами
- Управление контейнерами
- Взаимодействие с другими частями системы
- Interactive mode
- Ports
- Volumes
- Переменные окружения
- Подготовка собственного образа
- 1. Конечная структура папки, на основе файлов которой соберется образ, выглядит так:
- 2. Создание Dockerfile
- FROM
- RUN
- COPY
- WORKDIR
- CMD
- 3. Сборка
- 4. Публикация
- Docker Compose
- В бою
- Докер под капотом
- Дополнительные ссылки
Docker — программа, позволяющая операционной системе запускать процессы в изолированном окружении на базе специально созданных образов. Несмотря на то, что технологии, лежащие в основе Докера появились до него, именно Докер произвел революцию в том, как сегодня создается инфраструктура проектов, собираются и запускаются сервисы.
(В статье намеренно опущены многие детали, чтобы не грузить информацией, которая не нужна для ознакомления).
Установка
Докер поставляется в виде Community Edition (CE) и Enterprise Edition (EE). Нас интересует CE. На главной странице этой версии доступны ссылки для скачивания под все популярные платформы. Выберите вашу и установите Докер.
В работе Докера есть одна деталь, которую важно знать при установке на Mac и Linux. По умолчанию Докер работает через unix сокет. В целях безопасности сокет закрыт для пользователей, не входящих в группу docker. И хотя установщик добавляет текущего пользователя в эту группу автоматически, Докер сразу не заработает. Дело в том, что если пользователь меняет группу сам себе, то ничего не изменится до тех пор, пока пользователь не перелогинится. Такова особенность работы ядра. Для проверки того, в какие группы входит ваш пользователь, можно набрать команду id
.
Проверить успешность установки можно командой docker info
:
$ docker info
Containers: 22
Running: 2
Paused: 0
Stopped: 20
Images: 72
Server Version: 17.12.0-ce
Storage Driver: overlay2
Backing Filesystem: extfs
Supports d_type: true
Native Overlay Diff: true
Logging Driver: json-file
Cgroup Driver: cgroupfs
...
Она выдает довольно много информации о конфигурации самого Докера и статистику работы.
Запуск
На этом этапе команды на выполнение даются “как есть” без объяснения деталей. Подробнее о том, как их формировать и
что в них входит разбирается позже.
Начнем с самого простого варианта:
$ docker run -it nginx bash
root@a6c26812d23b:/#
При первом вызове данная команда начнет скачивать образ (image) nginx, поэтому придется немного подождать. После того, как образ скачается, запустится bash, и вы окажетесь внутри контейнера (container).
Побродите по файловой системе, посмотрите папку /etc/nginx
. Как видите, её содержимое не совпадает с тем, что находится у вас на компьютере. Эта файловая система появилась из образа nginx. Всё, что вы сделаете здесь внутри, никак не затронет вашу основную файловую систему. Вернуться в родные скрепы можно командой exit
.
Теперь посмотрим вариант вызова команды cat
, выполненной уже в другом контейнере, но тоже запущенном из образа nginx:
$ docker run nginx cat /etc/nginx/nginx.conf
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
...
$
Команда выполняется практически мгновенно, так как образ уже загружен. В отличие от предыдущего старта, где запускается баш и начинается интерактивная сессия внутри контейнера, запуск команды cat /etc/nginx/nginx.conf
для образа nginx выведет на экран содержимое указанного файла (взяв его из файловой системы запущенного контейнера) и вернет управление в то место, где вы были. Вы не окажетесь внутри контейнера.
Последний вариант запуска будет таким:
# Обратите внимание на то, что после имени образа не указана никакая команда.
# Такой подход работает в случае если команда на запуск прописана в самом образе
$ docker run -p 8080:80 nginx
Данная команда не возвращает управление, потому что стартует nginx. Откройте браузер и наберите localhost:8080
. Вы увидите как загрузилась страница Welcome to nginx!. Если в этот момент снова посмотреть в консоль, где был запущен контейнер, то можно увидеть, что туда выводится лог запросов к localhost:8080
. Остановить nginx можно командой Ctrl + C.
Несмотря на то, что все запуски выполнялись по-разному и приводили к разным результатам, общая схема их работы — одна. Докер при необходимости автоматически скачивает образ (первый аргумент после docker run
) и на основе него стартует контейнер с указанной командой.
Образ — самостоятельная файловая система. Пока мы используем готовые образы, но потом научимся создавать их самостоятельно.
Контейнер — запущенный процесс операционной системы в изолированном окружении с подключенной файловой системой из образа.
Повторюсь, что контейнер — всего лишь обычный процесс вашей операционной системы. Разница лишь в том, что благодаря возможностям ядра (о них в конце) Докер стартует процесс в изолированном окружении. Контейнер видит свой собственный список процессов, свою собственную сеть, свою собственную
краткий экскурс в историю и основные абстракции / Блог компании Southbridge / Хабр
10 августа в Слёрм стартовал видеокурс по Docker, в котором мы разбираем его полностью — от основных абстракций до параметров сети.
В этой статье поговорим об истории появления Docker и его основных абстракциях: Image, Cli, Dockerfile. Лекция рассчитана на новичков, поэтому вряд ли будет интересна опытным пользователям. Здесь не будет крови, аппендикса и глубокого погружения. Самые основы.
Что такое Docker
Посмотрим на определение Docker из Википедии.
Docker — это программное обеспечение для автоматизации развёртывания и управления приложениями в средах с поддержкой контейнеризации.
Из этого определения ничего непонятно. Особенно непонятно, что значит «в средах с поддержкой контейнеризации». Чтобы разобраться, вернёмся в прошлое. Начнём с эпохи, которую я условно называю «Монолитной эрой».
Монолитная эра
Монолитная эра — это начало 2000-х, когда все приложения были монолитными, с кучей зависимостей. Разработка шла долго. При этом серверов было не так много, мы все их знали по именам и мониторили. Есть такое забавное сравнение:
Pets — это домашние животные. В монолитной эре мы относились к своим серверам, как к домашним животным, холили и лелеяли, пылинки сдували. А для лучшего управления ресурсами использовали виртуализацию: брали сервер и пилили на несколько виртуальных машин, тем самым обеспечивая изоляцию окружения.
Системы виртуализации на базе гипервизора
Про системы виртуализации наверняка все слышали: VMware, VirtualBox, Hyper-V, Qemu KVM и т. д. Они обеспечивают изоляцию приложений и управление ресурсами, но у них есть и минусы. Чтобы сделать виртуализацию, нужен гипервизор. А гипервизор — это оверхед ресурсов. Да и сама виртуальная машина обычно целая махина — тяжелый образ, на нём операционная система, Nginx, Apache, возможно и MySQL. Образ большой, виртуальной машиной неудобно оперировать. Как следствие, работа с виртуалками может быть медленной. Чтобы решить эту проблему, создали системы виртуализации на уровне ядра.
Системы виртуализации на уровне ядра
Виртуализацию на уровне ядра поддерживают системы OpenVZ, Systemd-nspawn, LXC. Яркий пример такой виртуализации — LXC (Linux Containers).
LXC — система виртуализации на уровне операционной системы для запуска нескольких изолированных экземпляров операционной системы Linux на одном узле. LXC не использует виртуальные машины, а создаёт виртуальное окружение с собственным пространством процессов и сетевым стеком.
По сути LXC создаёт контейнеры. В чём разница между виртуальными машинами и контейнерами?
Контейнер не подходит для изолирования процессов: в системах виртуализации на уровне ядра находят уязвимости, которые позволяют вылезти из контейнера на хост. Поэтому если вам нужно что-то изолировать, то лучше использовать виртуалку.
Различия между виртуализацией и контейнеризацией можно увидеть на схеме.
Бывают аппаратные гипервизоры, гипервизоры поверх ОС и контейнеры.
«Железные» гипервизоры — это крутая штука, если вы действительно хотите что-то изолировать. Потому что там есть возможность изолировать на уровне страниц памяти, процессоров.
Есть гипервизоры как программа, и есть контейнеры, о них мы и будем говорить дальше. В системах контейнеризации гипервизора нет, но есть Container Engine, который создаёт контейнеры и управляет ими. Штука это более легковесная, поэтому за счет работы с ядром оверхед меньше, или его нет совсем.
Что используется для контейнеризации на уровне ядра
Основные технологии, которые позволяют создавать изолированный от других процессов контейнер, — это Namespaces и Control Groups.
Namespaces: PID, Networking, Mount и User. Есть ещё, но для простоты понимания остановимся на этих.
PID Namespace ограничивает процессы. Когда мы, например, создаём PID Namespace, помещаем туда процесс, то он становится с PID 1. Обычно в системах PID 1 — это systemd или init. Соответственно, когда мы помещаем процесс в новый namespace, он тоже получает PID 1.
Networking Namespace позволяет ограничить/изолировать сеть и внутри уже размещать свои интерфейсы. Mount — это ограничение по файловой системе. User — ограничение по юзерам.
Control Groups: Memory, CPU, IOPS, Network — всего около 12 настроек. Иначе их ещё называют Cgroups («Cи-группы»).
Control Groups управляют ресурсами для контейнера. Через Control Groups мы можем сказать, что контейнер не должен потреблять больше какого-то количества ресурсов.
Чтобы контейнеризация полноценно работала, используются дополнительные технологии: Capabilities, Copy-on-write и другие.
Capabilities — это когда мы говорим процессу, что он может делать, а чего не может. На уровне ядра это просто битовые карты со множеством параметров. Например, пользователь root имеет полные привилегии, может делать всё. Сервер времени может изменять системное время: у него есть capabilities на Time Capsule, и всё. С помощью привилегий можно гибко настроить ограничения для процессов, и тем самым обезопасить себя.
Система Copy-on-write позволяет нам работать с образами Docker, использовать их более эффективно.
На данный момент Docker имеет проблемы с совместимостью Cgroups v2, поэтому в статье рассматриваются именно Cgroups v1.
Но вернёмся к истории.
Когда появились системы виртуализации на уровне ядра, их начали активно применять. Оверхед на гипервизор пропал, но некоторые проблемы остались:
- большие образы: в ту же OpenVZ толкают операционку, библиотеки, кучу разного софта, и в итоге образ всё равно получается немаленьким;
- нет нормального стандарта упаковки и доставки, поэтому остаётся проблема зависимостей. Бывают ситуации, когда два куска кода используют одну библиотеку, но с разными версиями. Между ними возможен конфликт.
Чтобы все эти проблемы решить, пришла следующая эра.
Эра контейнеров
Когда наступила Эра контейнеров, сменилась философия работы с ними:
- Один процесс — один контейнер.
- Все нужные процессу зависимости доставляем в его контейнер. Это требует распиливать монолиты на микросервисы.
- Чем меньше образ, тем лучше — меньше возможных уязвимостей, быстрее раскатывается и так далее.
- Инстансы становятся эфемерными.
Помните, я говорил про pets vs cattle? Раньше инстансы были подобны домашним животным, а теперь стали как cattle — скот. Раньше был монолит — одно приложение. Теперь это 100 микросервисов, 100 контейнеров. У каких-то контейнеров может быть по 2-3 реплики. Нам становится не столь важно контролировать каждый контейнер. Нам скорее важна доступность самого сервиса: того, что делает этот набор контейнеров. Это меняет подходы в мониторинге.
В 2014-2015 годах случился расцвет Docker — той технологии, о которой мы и будем сейчас говорить.
Docker изменил философию и стандартизировал упаковку приложения. С помощью Docker мы можем упаковать приложение, отправить его в репозиторий, скачать оттуда, развернуть.
В Docker-контейнер мы закладываем всё необходимое, поэтому решается проблема зависимостей. Docker гарантирует воспроизводимость. Я думаю, многие сталкивались с невоспроизводимостью: у тебя всё работает, пушишь на продакшен, там это перестает работать. С Docker эта проблема уходит. Если твой Docker-контейнер запускается и делает то, что требуется делать, то с большой долей вероятности он запустится на продакшене и там сделает то же самое.
Отступление про оверхед
По поводу оверхед постоянно идут споры. Кто-то считает, что Docker не несёт дополнительную нагрузку, так как использует ядро Linux и все его процессы, необходимые для контейнеризации. Мол, «если вы говорите, что Docker — это оверхед, то тогда и ядро Linux оверхед».
С другой стороны, если углубиться, то в Docker и правда есть несколько вещей, про которые с натяжкой можно сказать, что это оверхед.
Первое — это PID namespace. Когда мы в namespace помещаем какой-то процесс, ему присваивается PID 1. В то же время у этого процесса есть ещё один PID, который находится на хостовом namespace, за пределами контейнера. Например, мы запустили в контейнере Nginx, он стал PID 1 (мастер-процесс). А на хосте у него PID 12623. И сложно сказать, насколько это оверхед.
Вторая штука — это Cgroups. Возьмём Cgroups по памяти, то есть возможность ограничивать контейнеру память. При её включении активируются счётчики, memory accounting: ядру надо понимать, сколько страниц выделено, а сколько ещё свободно для этого контейнера. Это возможно оверхед, но точных исследований о том, как он влияет на производительность, я не встречал. И сам не замечал, что приложение, запущенное в Docker, вдруг резко теряло в производительности.
И ещё одно замечание о производительности. Некоторые параметры ядра прокидываются с хоста в контейнер. В частности, некоторые сетевые параметры. Поэтому если вы хотите запустить в Docker что-то высокопроизводительное, например то, что будет активно использовать сеть, то вам, как минимум, надо эти параметры подправить. Какой-нибудь nf_conntrack, к примеру.
О концепции Docker
Docker состоит из нескольких компонентов:
- Docker Daemon — то самое Container Engine; запускает контейнеры.
- Docker CLI — утилита по управлению Docker.
- Dockerfile — инструкция по тому, как собирать образ.
- Image — образ, из которого раскатывается контейнер.
- Container.
- Docker registry — хранилище образов.
Схематично это выглядит примерно вот так:
На Docker_host работает Docker daemon, запускает контейнеры. Есть Client, который передаёт команды: собери образ, скачай образ, запусти контейнер. Docker daemon ходит в registry и выполняет их. Docker-клиент может обращаться и локально (к юникс-сокету), и по TCP с удалённого хоста.
Пройдёмся по каждому компоненту.
Docker daemon (демон) — это серверная часть, она работает на хост-машине: скачивает образы и запускает из них контейнеры, создаёт сеть между контейнерами, собирает логи. Когда мы говорим «создай образ», этим тоже занимается демон.
Docker CLI — клиентская часть Docker, консольная утилита для работы с демоном. Повторю, она может работать не только локально, но и по сети.
Базовые команды:
docker ps — показать контейнеры, которые сейчас запущены на Docker-хосте.
docker images — показать образы, скачанные локально.
docker search <> — поиск образа в registry.
docker pull <> — скачать образ из registry на машину.
docker build <</path/to/dir>> — собрать образ.
docker run <> — запуск контейнер.
docker rm <> — удалить контейнер.
docker logs <> — логи контейнера
docker start/stop/restart <> — работа с контейнером
Если вы освоите эти команды и будете уверенно ими пользоваться, то считайте, что на 70% освоили Docker на уровне пользователя.
Dockerfile — инструкция для создания образа. Почти каждая команда инструкции — новый слой. Посмотрим на примере.
Примерно так выглядит Dockerfile: слева команды, справа — аргументы. Каждая команда, что здесь есть (и вообще пишется в Dockerfile), создаёт новый слой в Image.
Даже глядя на левую часть, можно примерно понять, что происходит. Мы говорим: «создай нам папку» — это один слой. «Сделай папку рабочей» — это ещё один слой, и так далее. Слоёный пирог упрощает жизнь. Если я создам ещё один Dockerfile и в последней строчке что-то изменю — запущу не «python» «main.py», а что-нибудь другое, или установлю зависимости из другого файла — то предыдущие слои будут переиспользованы, как кеш.
Image — это упаковка контейнера, из образа запускаются контейнеры. Если смотреть на Docker с точки зрения пакетного менеджера (как будто мы работаем с deb или rpm-пакетами), то image — это по сути rpm-пакет. Через yum install мы можем поставить приложение, удалить его, найти в репозитории, скачать. Здесь примерно то же самое: из образа запускаются контейнеры, они хранятся в Docker registry (по аналогии с yum, в репозитории), и каждый image имеет хеш SHA-256, имя и тег.
Image собирается по инструкции из Dockerfile. Каждая инструкция из Dockerfile создаёт новый слой. Слои могут использоваться повторно.
Docker registry — это репозиторий образов Docker. По аналогии с ОС, у Docker есть общедоступный стандартный реестр — dockerhub. Но можно собрать свой репозиторий, свой Docker registry.
Container — то, что запускается из образа. По инструкции из Dockerfile собрали образ, затем мы его из этого образа запускаем. Этот контейнер изолирован от остальных контейнеров, он должен содержать в себе всё необходимое для работы приложения. При этом один контейнер — один процесс. Случается, что приходится делать два процесса, но это несколько противоречит идеологии Docker.
Требование «один контейнер — один процесс» связано с PID Namespace. Когда в Namespace запускается процесс с PID 1, если он вдруг умрёт, то весь контейнер тоже умирает. Если же там запущено два процесса: один живёт, а второй умер, то контейнер всё равно продолжит жить. Но это к вопросу Best Practices, мы про них поговорим в других материалах.
Более детально изучить особенности и полную программу курса можно по ссылке: «Видеокурс по Docker».
Автор: Марсель Ибраев, сертифицированный администратор Kubernetes, практикующий инженер в компании Southbridge, спикер и разработчик курсов Слёрм.
Docker. Начало / Хабр
Примерно такие же эмоции я и мои коллеги испытывали, когда начинали работать с Docker. В подавляющем большинстве случаев это происходило от недостатка понимания основных механизмов, поэтому его поведение казалось нам непредсказуемым. Сейчас страсти поутихли и вспышки ненависти происходят все реже и все слабее. Более того, постепенно мы на практике оцениваем его достоинства и он начинает нам нравиться… Чтобы снизить степень первичного отторжения и добиться максимального эффекта от использования, нужно обязательно заглянуть на кухню Docker’a и хорошенько там осмотреться.
Начнем с того, для чего же нам нужен Docker:
- изолированный запуск приложений в контейнерах
- упрощение разработки, тестирования и деплоя приложений
- отсутствие необходимости конфигурировать среду для запуска — она поставляется вместе с приложением — в контейнере
- упрощает масштабируемость приложений и управление их работой с помощью систем оркестрации контейнеров.
Предыстрория
Для изоляции процессов, запущенных на одном хосте, запуска приложений, предназначенных для разных платформ, можно использовать виртуальные машины. Виртуальные машины делят между собой физические ресурсы хоста:
- процессор,
- память,
- дисковое пространство,
- сетевые интерфейсы.
На каждой ВМ устанавливаем нужную ОС и запускаем приложения. Недостатком такого подхода является то, что значительная часть ресурсов хоста расходуется не на полезную нагрузку(работа приложений), а на работу нескольких ОС.
Контейнеры
Альтернативным подходом к изоляции приложений являются контейнеры. Само понятие контейнеров не ново и давно известно в Linux. Идея состоит в том, чтобы в рамках одной ОС выделить изолированную область и запускать в ней приложение. В этом случае говорим о виртуализации на уровне ОС. В отличие от ВМ контейнеры изолированно используют свой кусочек ОС:
- файловая система
- дерево процессов
- сетевые интерфейсы
- и др.
Т.о. приложение, запущенное в контейнере думает, что оно одно во всей ОС. Изоляция достигается за счет использования таких Linux-механизмов, как namespaces и control groups. Если говорить просто, то namespaces обеспечивают изоляцию в рамках ОС, а control groups устанавливают лимиты на потребление контейнером ресурсов хоста, чтобы сбалансировать распределение ресурсов между запущенными контейнерами.
Т.о. контейнеры сами по себе не являются чем-то новым, просто проект Docker, во-первых, скрыл сложные механизмы namespaces, control groups, а во-вторых, он окружен экосистемой, обеспечивающей удобное использование контейнеров на всех стадиях разработки ПО.
Образы
Образ в первом приближении можно рассматривать как набор файлов. В состав образа входит все необходимое для запуска и работы приложения на голой машине с докером: ОС, среда выполнения и приложение, готовое к развертыванию.
Но при таком рассмотрении возникает вопрос: если мы хотим использовать несколько образов на одном хосте, то будет нерационально как с точки зрения загрузки, так и с точки зрения хранения, чтобы каждый образ тащил все необходимое для своей работы, ведь большинство файлов будут повторяться, а различаться — только запускаемое приложение и, возможно, среда выполнения. Избежать дублирования файлов позволяет структура образа.
Образ состоит из слоев, каждый из которых представляет собой неизменяемую файловую систему, а по-простому набор файлов и директорий. Образ в целом представляет собой объединенную файловую систему (Union File System), которую можно рассматривать как результат слияния файловых систем слоев. Объединенная файловая система умеет обрабатывать конфликты, например, когда в разных слоях присутствуют файлы и директории с одинаковыми именами. Каждый следующий слой добавляет или удаляет какие то файлы из предыдущих слоев. В данном контексте «удаляет» можно рассматривать как «затеняет», т.е. файл в нижележащем слое остается, но его не будет видно в объединенной файловой системе.
Можно провести аналогию с Git: слои — это как отдельные коммиты, а образ в целом — результат выполнения операции squash. Как мы увидим дальше, на этом параллели с Git не заканчиваются. Существуют различные реализации объединенной файловой системы, одна из них — AUFS.
Для примера рассмотрим образ произвольного .NET приложения MyApplication: первым слоем является ядро Linux, далее следуют слои ОС, среды исполнения и уже самого приложения.
Слои являются read only и, если в слое MyApplication нужно изменить файл, находящийся в слое dotnet, то файл сначала копируется в нужный слой, а потом в нем изменяется, оставаясь в исходном слое в первозданном виде.
Неизменяемость слоев позволяет использовать их всеми образами на хосте. Допустим MyApplication — это веб-приложение, которое использует БД и взаимодействует также с NodeJS сервером.
Совместное использование проявляется также и при скачивании образа. Первым загружается манифест, который описывает какие слои входят в образ. Далее скачиваются только те слои из манифеста, которых еще нет локально. Т.о. если мы для MyApplication уже скачали ядро и ОС, то для PostgreSQL и Node.js эти слои уже загружаться не будут.
Подытожим:
- Образ — это набор файлов, необходимых для работы приложения на голой машине с установленным Docker.
- Образ состоит из неизменяемых слоев, каждый из которых добавляет/удаляет/изменяет файлы из предыдущего слоя.
- Неизменяемость слоев позволяет их использовать совместно в разных образах.
Docker-контейнеры
Docker-контейнер строится на основе образа. Суть преобразования образа в контейнер состоит в добавлении верхнего слоя, для которого разрешена запись. Результаты работы приложения (файлы) пишутся именно в этом слое.
Например, мы создали на основе образа с PostgreSQL сервером контейнер и запустили его. Когда мы создаем БД, то соответствующие файлы появляются в верхнем слое контейнера — слое для записи.
Можно провести и обратную операцию: из контейнера сделать образ. Верхний слой контейнера отличается от остальных только лишь разрешением на запись, в остальном это обычный слой — набор файлов и директорий. Делая верхний слой read only, мы преобразуем контейнер в образ.
Теперь я могу перенести образ на другую машину и запустить. При этом на сервере PostgreSQL можно будет увидеть БД, созданные на предыдущем этапе. Когда при работе контейнера будут внесены изменения, то файл БД будет скопирован из неизменяемого слоя с данными в слой для записи и там уже измененен.
Docker
Когда мы устанавливаем докер на локальную машину, то получаем клиент (CLI) и http-сервер, работающий как демон. Сервер предоставляет REST API, а консоль просто преобразует введенные команды в http-запросы.
Registry
Registry — это хранилище образов. Самым известным является DockerHub. Он напоминает GitHub, только содержит образы, а не исходный код. На DockerHub также есть репозитории, публичные и приватные, можно скачивать образы (pull), заливать изменения образов (push). Скачанные однажды образы и собранные на их основе контейнеры хранятся локально, пока не будут удалены вручную.
Существует возможность создания своего хранилища образов, тогда при необходимости Docker будет искать там образы, которых еще нет локально. Надо сказать, что при использовании Docker хранилище образов становится важнейшим звеном в CI/CD: разработчик делает коммит в репозиторий, запускаются тесты. Если тесты прошли успешно, то на основе коммита обновляется существующий или собирается новый образ с последующим деплоем. Причем в registry обновляются не целые образы, а только необходимые слои.
При этом важно не ограничивать восприятие образа как некой коробки в которой приложение просто доставляется до пункта назначения и потом запускается. Приложение может и собираться внутри образа (правильнее сказать внутри контейнера, но об этом чуть позже). На схеме выше сервер, занимающийся сборкой образов, может иметь только установленный Docker, а не различные среды, платформы и приложения, необходимые для сборки разных компонентов нашего приложения.
Dockerfile
Dockerfile представляет собой набор инструкций, на основе которых строится новый образ. Каждая инструкция добавляет новый слой к образу. Для примера рассмотрим Dockerfile, на основе которого мог бы быть создан образ рассмотренного ранее .NET-приложения MyApplication:
FROM microsoft/aspnetcore
WORKDIR /app
COPY bin/Debug/publish .
ENTRYPOINT["dotnet", "MyApplication.dll"]
Рассмотрим отдельно каждую инструкцию:
- определяем базовый образ, на основе которого будем строить свой. В данном случае берем microsoft/aspnetcore — официальный образ от Microsoft, который можно найти на DockerHub
- задаем рабочую директорию внутри образа
- копируем предварительно спаблишенное приложение MyApplication в рабочую директорию внутри образа. Сначала пишется исходная директория — путь относительно контекста, указанного в команде
docker build
, а вторым аргументом — целевая директория внутри образа, в данном случае точка обозначает рабочую директорию - конфигурируем контейнер как исполняемый: в нашем случае для запуска контейнера будет выполнена команда
dotnet MyApplication.dll
Если в директории с Dockerfile выполнить команду docker build
, то мы получим образ на основе microsoft/aspnetcore, к которому будет добавлено еще три слоя.
Рассмотрим еще один Dockerfile, который демонстрирует прекрасную возможность Docker, обеспечивающую легковесность образов. Подобный файл генерирует VisualStudio 2017 для проекта с поддержкой контейнеров и он позволяет собирать образ из исходного кода приложения.
FROM microsoft/aspnetcore-build:2.0 AS publish
WORKDIR /src
COPY . .
RUN dotnet restore
RUN dotnet publish -o /publish
FROM microsoft/aspnetcore:2.0
WORKDIR /app
COPY --from=publish /publish .
ENTRYPOINT ["dotnet", "MyApplication.dll"]
Инструкции в файле разбиты на две секции:
- Определение образа для сборки приложения: microsoft/aspnetcore-build. Данный образ предназначен для сборки, паблиша и запуска .NET приложений и согласно DockerHub с тегом 2.0 имеет размер 699 MB. Далее происходит копирование исходных файлов приложения внутрь образа и внутри него выполняются команды
dotnet restore
иdotnet publish
с размещением результатов в директории /publish внутри образа. - Определяется базовый образ, в данном случае это microsoft/aspnetcore, который содержит в себе только среду исполнения и согласно DockerHub с тегом 2.0 имеет размер всего 141 MB. Далее определяется рабочая директория и в нее копируется результат предыдущей стадии (ее имя указывается в аргументе
--from
), определяется команда запуска контейнера и все — образ готов.
В итоге изначально имея исходный код приложения, на основе тяжелого образа с SDK было спаблишено приложение, а потом результат размещен поверх легкого образа, содержащего только среду исполнения!
Напоследок хочу отметить, что намеренно для простоты оперировал понятием образ, рассматривая работу с Dockerfile. На самом деле изменения, вносимые каждой инструкцией происходят конечно же не в образе (ведь в нем только неизменяемые слои), а в контейнере. Механизм такой: из базового образа создается контейнер (добавляется ему слой для записи), выполняется инструкция в данном слое (она может добавлять файлы в слой для записи: COPY
или нет: ENTRYPOINT
), вызывается команда docker commit
и получается образ. Процесс создания контейнера и коммита в образ повторяется для каждой инструкции в файле. В итоге в процессе формирования конечного образа создается столько промежуточных образов и контейнеров, сколько инструкций в файле. Все они автоматически удаляются после окончания сборки конечного образа.
Заключение
Конечно же Docker не панацея и его использование должно быть оправдано и мотивировано не только желанием использовать современную технологию, о которой многие говорят. При этом я уверен, что Docker, примененный грамотно и к месту, может принести много пользы на всех стадиях разработки ПО и облегчить жизнь всем участникам процесса.
Надеюсь смог раскрыть базовые моменты и заинтересовать к дальнейшему изучению вопроса. Конечно же для овладения Docker одной этой статьи недостаточно, но, надеюсь, она станет одним из элементов пазла для осознания общей картины происходящего в мире контейнеров под управлением Docker.
Ссылки
- Документация Docker
- Механизм namespaces
- Механизм control groups
- Статья о Docker
Просто о сложном — Docker
Docker — платформа для запуска приложений в изолированных контейнерах.
Простой пример. Будь докер в жизни, вы бы смогли поставить посреди комнаты пакет, а лучше сразу несколько, где в первом у вас будет взрывчатка, а во втором то, что вы хотите подорвать и вот сами вы выступаете в роли спички, фитиля и волшебника, который делает так, чтобы содержимое второго пакета взорвалось, не унеся с собой квартиру и жизнь волшебника за компанию.
Только пакетов не обязательно 2 и в них не обязательно что-то опасное, да и комната (сервер) тоже может быть не одна. Реальные примеры с применением будут описаны ниже.
Нет ничего сложного в использовании докера. Для большинства задач нужны совсем поверхностные знания, которые можно получить, почитав всего пару статей или, еще лучше, раздел Get started официальной документации, затем подкрепить все эти знания практикой. Например, запустить MySQL сервер + Adminer в один клик на этой странице. Там есть кнопочка. Но это только для начала.
Photo by Danielle MacInnes / Unsplash
Для вас это может быть очевидным, но для меня, как человека, который для всех задач искал панель управления было тяжело осознать одну простую вещь:
С Docker придется понять, что теперь не панель управления, а вы должны будете придумать, где разместить ваши данные из контейнера, будь то сайт или что-то еще
Тоесть, теперь не будет никаких кнопочек а-ля «Файловый менеджер», которые сразу откроют вам папочку с данными ваших контейнеров. Вы должны сами придумать, где будете работать. У меня это ~/dockerfiles/somefolder
.
И маленькое замечание насчет безопасности — вам придется выполнять все команды или от рута или от sudo или добавить вашего пользователя в группу docker (лучше последнее)
А еще насчет портов. Есть Docker port, а есть Published port. Docker port это порт, который контейнер будет считать, что использует, а Published это порт, через который будет доступен Docker port на родительской машине. Простыми словами, это проброс портов
Контейнер
Это как виртуальная машина. Его можно удалить, остановить, возобновить, подключиться к терминалу. В общем, делать все, что и с обычным linux сервером или виртуальной машиной
Dockerfile
Это набор инструкций, как нужно создавать контейнер. Пример такого файла можно увидеть здесь. Описание основных инструкций вот здесь
docker-compose.yml
В докере один Dockerfile это один сервис. Если вам нужен один сервис, например, торрентокачалка или генерация LetsEncrypt сертификата, вы можете запустить его через docker run
. Но если у вас приложение, состоящее из нескольких сервисов (PHP + Nginx + MySQL), то запускать их все по очереди длинными командами с кучей параметров было бы, как минимум, неудобно. Этот файл объединяет все в одном месте и управляется через команду docker-compose
Я выделил популярные команды. Они частично будут описаны ниже
Репозиторий
Есть репозитории, а есть Automated builds. По-простому, репозитории это готовые сборки систем. Например, чистый Ubuntu. Некоторые репозитории это сразу система с установленным приложением, например, CMS Ghost. На Docker HUB репозиторий это куча файлов, а Automated build это всего лишь Dockerfile
Automated build
На первый взгляд ничем не отличаются от репозиториев. Мне даже трудно объяснить разницу. По-обезьяньи я понимаю это так: «у репозиториев я не могу посмотреть Dockerfile, а у autobuilds могу». Automated builds это те же репозитории, только с них вы можете без проблем забрать Dockerfile и поправить его под свои нужды.
Также со временем Automated build может перестать работать, если какая-то инструкция в нем устареет (например, Dockerfile писался под Ubuntu 14.04 (FROM ubuntu:latest
), а со временем ubuntu:latest обновилась до 18.04 и вжух, проблемы). Репозитории в этом плане монолитные. Какими залиты, такими и будут даже через 100 лет.
Еще есть всякие инструкции для Dockerfile, вроде ADD
и COPY
, которые добавляют файлы в контейнер во время его создания и которые не работают для репов
Пример Automated Build: cpuminer-multi
Swarm
Вы можете даже ни разу не столкнуться с этим на практике, но на будущее — это что-то вроде кластера. Короче, связка серверов с установленным Docker.
В нем есть главная нода и подчиненные. Главная это та, на которой выполнен docker swarm init
, а подчиненные те, которые присоединились через docker swarm join
и готовы принимать указания от главной (docker stack deploy
)
- Можно без проблем изучать новые фишки, до которых раньше руки не доходили из-за сложности или не хотелось засорять систему. Например хотели простой блог на JavaScript, торрент качалку или даже целый комбайн из приложений с кучей мусора и зависимостями без заморочки — докер.
- Если вы раньше использовали VirtualBox для запуска приложений, то Docker его не только заменит, но и сделает все быстрее и проще
- Он поможет запустить приложение любой сложности на любой системе всего в пару нажатий клавиш при помощи всего лишь одного Dockerfile файла. Или docker-compose.yml, если нужно запустить пачку сервисов.
- Благодаря докеру вы можете собирать данные кучи приложений в одном, нужном вам месте, благодаря чему их проще будет переносить.
- Или, например, у вас есть веб приложение с кучей заточенных под вас настроек, всяких баз данных, веб серверов, фреймворков и тд и вам нужно расширяться, копировать все это на другие сервера, прописывать все заново. С докером вы выполняете
docker swarm init
на главной ноде иdocker swarm join
на всех подчиненных, затем с главной передаете всем остальным задачу деплоя ваших приложений/сервисов при помощи пары команд. - Еще пример. Вы когда-нибудь использовали WAMP? Это связка
Apache + PHP + PHPMyAdmin + MySQL
и еще пары ништяков, которые помогают PHP разработчкам писать и тестить сайты, сидя на винде без всяких виртуалок. Так вот WAMP полезен только на винде и только для PHP. А Docker полезен хоть на Windows, хоть на Linux, хоть на Mac OS и для чего угодно, а не только PHP разработчиков
Кстати, этот блог запущен внутри Docker контейнера, а рядом с ним крутится еще контейнер с Nginx, контейнер с MariaDB, при необходимости, запускаю еще контейнер с Adminer (Аналог phpmyadmin) и LetsEncrypt генерилкой. Благодаря этому я могу купить еще хоть 100 VDS с Docker, одной командой подключить их к одному swarm’у (кластеру) и запустить сразу на всех мой блог так же в одну команду. Тогда даже если 99 нод «умрут», блог все равно будет доступен на последней.
Запуск контейнера
- Фоновый (Запустили и отключились detach)
docker run -d mysql
- Со своим названием
docker run --name database mysql
- Этот удалится после отключения от него или выполнения задачи
docker run -rm ubuntu pwd
- Проброс порта с контейнера на хост. Слева порт на хосте (Published)
docker run -p 8080:80 -d nginx
- Остановка работающего (Данные внутри сохраняются. Запуск через start)
docker stop NAME
- Удаление контейнера (Проброшенные в него файлы сохраняются)
docker rm NAME
(-f удаляет даже если контейнер запущен)
Отладка
- Подключение к терминалу контейнера
docker exec -it NAME bash
- Посмотреть логи внутри контейнера (STDOUT, STDERR)
docker logs -f NAME
Управление образами
- Сборка образа контейнера на базе Dockerfile (точка в конце нужна)
docker build --tag image-name .
- Список
docker images -a
- Удаление
docker image rm NAME
(-f force) - Переименование
docker image tag SOURCE[:TAG] TARGET[:TAG]
docker-compose
- Создание/обновление контейнеров приложения в фоне (-d)
docker-compose up -d APP_NAME
- Остановка
docker-compose stop
- Запуск остановленного приложения
docker-compose start
- Остановка и удаление
docker-compose down
- Логи всех сервисов приложения
docker-compose logs -f
Другие полезности
- Работающие и остановленные контейнеры
docker ps -a
(Без -a только работающие) - Показать данные контейнера (Тут можно найти его IP, пути к volumes и кучу другого)
docker inspect NAME
Чистка мусора
- Удалить все остановленные контейнеры
docker rm $(docker ps -a -q)
- Удалить незакрепленные ни за каким контейнером volumes
docker volume rm $(docker volume ls -f dangling=true -q)
- Удалить неиспользуемые сети
docker network prune
- Удалить все неиспользуемые данные
docker system prune -a
- Удалить контейнеры, что остановлены больше часа назад + образы, с которыми не работает ни один контейнер (продвинутый способ)
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -v /etc:/etc:ro spotify/docker-gc
Небольшой лайфхак, как разобраться в Docker со скоростью света — изучайте чужие Dockerfile и docker-compose.yml и не ленитесь читать официальные документации, там все написано максимально кратко. Нельзя выучить докер за 5 минут, но сколько бы времени вы не потратили на его изучение, это того стоит.
Руководство по Docker Compose для начинающих / Блог компании RUVDS.com / Хабр
Автор статьи, перевод которой мы сегодня публикуем, говорит, что она предназначена для тех разработчиков, которые хотят изучить Docker Compose и идут к тому, чтобы создать своё первое клиент-серверное приложение с использованием Docker. Предполагается, что читатель этого материала знаком с основами Docker. Если это не так — можете взглянуть на эту серию материалов, на эту публикацию, где основы Docker рассмотрены вместе с основами Kubernetes, и на эту статью для начинающих.
Что такое Docker Compose?
Docker Compose — это инструментальное средство, входящее в состав Docker. Оно предназначено для решения задач, связанных с развёртыванием проектов.
Изучая основы Docker, вы могли столкнуться с созданием простейших приложений, работающих автономно, не зависящих, например, от внешних источников данных или от неких сервисов. На практике же подобные приложения — редкость. Реальные проекты обычно включают в себя целый набор совместно работающих приложений.
Как узнать, нужно ли вам, при развёртывании некоего проекта, воспользоваться Docker Compose? На самом деле — очень просто. Если для обеспечения функционирования этого проекта используется несколько сервисов, то Docker Compose может вам пригодиться. Например, в ситуации, когда создают веб-сайт, которому, для выполнения аутентификации пользователей, нужно подключиться к базе данных. Подобный проект может состоять из двух сервисов — того, что обеспечивает работу сайта, и того, который отвечает за поддержку базы данных.
Технология Docker Compose, если описывать её упрощённо, позволяет, с помощью одной команды, запускать множество сервисов.
Разница между Docker и Docker Compose
Docker применяется для управления отдельными контейнерами (сервисами), из которых состоит приложение.
Docker Compose используется для одновременного управления несколькими контейнерами, входящими в состав приложения. Этот инструмент предлагает те же возможности, что и Docker, но позволяет работать с более сложными приложениями.
Docker (отдельный контейнер) и Docker Compose (несколько контейнеров)
Типичный сценарий использования Docker Compose
Docker Compose — это, в умелых руках, весьма мощный инструмент, позволяющий очень быстро развёртывать приложения, отличающиеся сложной архитектурой. Сейчас мы рассмотрим пример практического использования Docker Compose, разбор которого позволит вам оценить те преимущества, которые даст вам использование Docker Compose.
Представьте себе, что вы являетесь разработчиком некоего веб-проекта. В этот проект входит два веб-сайта. Первый позволяет людям, занимающимся бизнесом, создавать, всего в несколько щелчков мышью, интернет-магазины. Второй нацелен на поддержку клиентов. Эти два сайта взаимодействуют с одной и той же базой данных.
Ваш проект становится всё популярнее, и оказывается, что мощности сервера, на котором он работает, уже недостаточно. В результате вы решаете перевести весь проект на другую машину.
К сожалению, нечто вроде Docker Compose вы не использовали. Поэтому вам придётся переносить и перенастраивать сервисы по одному, надеясь на то, что вы, в процессе этой работы, ничего не забудете.
Если же вы используете Docker Compose, то перенос вашего проекта на новый сервер — это вопрос, который решается выполнением нескольких команд. Для того чтобы завершить перенос проекта на новое место, вам нужно лишь выполнить кое-какие настройки и загрузить на новый сервер резервную копию базы данных.
Разработка клиент-серверного приложения с использованием Docker Compose
Теперь, когда вы знаете о том, для чего мы собираемся использовать Docker Compose, пришло время создать ваше первое клиент-серверное приложение с использованием этого инструмента. А именно, речь идёт о разработке небольшого веб-сайта (сервера) на Python, который умеет выдавать файл с фрагментом текста. Этот файл у сервера запрашивает программа (клиент), тоже написанная на Python. После получения файла с сервера программа выводит текст, хранящийся в нём, на экран.
Обратите внимание на то, что мы рассчитываем на то, что вы владеете основами Docker, и на то, что у вас уже установлена платформа Docker.
Приступим к работе над проектом.
▍1. Создание проекта
Для того чтобы построить ваше первое клиент-серверное приложение, предлагаю начать с создания папки проекта. Она должна содержать следующие файлы и папки:
- Файл
docker-compose.yml
. Это файл Docker Compose, который будет содержать инструкции, необходимые для запуска и настройки сервисов. - Папка
server
. Она будет содержать файлы, необходимые для обеспечения работы сервера. - Папка
client
. Здесь будут находиться файлы клиентского приложения.
В результате содержимое главной папки вашего проекта должно выглядеть так:
.
├── client/
├── docker-compose.yml
└── server/
2 directories, 1 file
▍2. Создание сервера
Тут мы, в процессе создания сервера, затронем некоторые базовые вещи, касающиеся Docker.
2a. Создание файлов
Перейдите в папку server
и создайте в ней следующие файлы:
- Файл
server.py
. В нём будет находиться код сервера. - Файл
index.html
. В этом файле будет находиться фрагмент текста, который должно вывести клиентское приложение. - Файл
Dockerfile
. Это — файл Docker, который будет содержать инструкции, необходимые для создания окружения сервера.
Вот как должно выглядеть содержимое вашей папки server/
:
.
├── Dockerfile
├── index.html
└── server.py
0 directories, 3 files
2b. Редактирование Python-файла.
Добавим в файл server.py
следующий код:
#!/usr/bin/env python3
# Импорт системных библиотек python.
# Эти библиотеки будут использоваться для создания веб-сервера.
# Вам не нужно устанавливать что-то особенное, эти библиотеки устанавливаются вместе с Python.
import http.server
import socketserver
# Эта переменная нужна для обработки запросов клиента к серверу.
handler = http.server.SimpleHTTPRequestHandler
# Тут мы указываем, что сервер мы хотим запустить на порте 1234.
# Постарайтесь запомнить эти сведения, так как они нам очень пригодятся в дальнейшем, при работе с docker-compose.
with socketserver.TCPServer(("", 1234), handler) as httpd:
# Благодаря этой команде сервер будет выполняться постоянно, ожидая запросов от клиента.
httpd.serve_forever()
Этот код позволяет создать простой веб-сервер. Он будет отдавать клиентам файл index.html
, содержимое которого позже будет выводиться на веб-странице.
2c. Редактирование HTML-файла
В файл index.html
добавим следующий текст:
Docker-Compose is magic!
Этот текст будет передаваться клиенту.
2d. Редактирование файла Dockerfile
Сейчас мы создадим простой файл Dockerfile
, который будет отвечать за организацию среды выполнения для Python-сервера. В качестве основы создаваемого образа воспользуемся официальным образом, предназначенным для выполнения программ, написанных на Python. Вот содержимое Dockerfile:
# На всякий случай напоминаю, что Dockerfile всегда должен начинаться с импорта базового образа.
# Для этого используется ключевое слово 'FROM'.
# Здесь нам нужно импортировать образ python (с DockerHub).
# В результате мы, в качестве имени образа, указываем 'python', а в качестве версии - 'latest'.
FROM python:latest
# Для того чтобы запустить в контейнере код, написанный на Python, нам нужно импортировать файлы 'server.py' и 'index.html'.
# Для того чтобы это сделать, мы используем ключевое слово 'ADD'.
# Первый параметр, 'server.py', представляет собой имя файла, хранящегося на компьютере.
# Второй параметр, '/server/', это путь, по которому нужно разместить указанный файл в образе.
# Здесь мы помещаем файл в папку образа '/server/'.
ADD server.py /server/
ADD index.html /server/
# Здесь мы воспользуемся командой 'WORKDIR', возможно, новой для вас.
# Она позволяет изменить рабочую директорию образа.
# В качестве такой директории, в которой будут выполняться все команды, мы устанавливаем '/server/'.
WORKDIR /server/
Теперь займёмся работой над клиентом.
▍3. Создание клиента
Создавая клиентскую часть нашего проекта, мы попутно вспомним некоторые основы Docker.
3a. Создание файлов
Перейдите в папку вашего проекта client
и создайте в ней следующие файлы:
- Файл
client.py
. Тут будет находиться код клиента. - Файл
Dockerfile
. Этот файл играет ту же роль, что и аналогичный файл в папке сервера. А именно, он содержит инструкцию, описывающую создание среды для выполнения клиентского кода.
В результате ваша папка client/
на данном этапе работы должна выглядеть так:
.
├── client.py
└── Dockerfile
0 directories, 2 files
3b. Редактирование Python-файла
Добавим в файл client.py
следующий код:
#!/usr/bin/env python3
# Импортируем системную библиотеку Python.
# Она используется для загрузки файла 'index.html' с сервера.
# Ничего особенного устанавливать не нужно, эта библиотека устанавливается вместе с Python.
import urllib.request
# Эта переменная содержит запрос к 'http://localhost:1234/'.
# Возможно, сейчас вы задаётесь вопросом о том, что такое 'http://localhost:1234'.
# localhost указывает на то, что программа работает с локальным сервером.
# 1234 - это номер порта, который вам предлагалось запомнить при настройке серверного кода.
fp = urllib.request.urlopen("http://localhost:1234/")
# 'encodedContent' соответствует закодированному ответу сервера ('index.html').
# 'decodedContent' соответствует раскодированному ответу сервера (тут будет то, что мы хотим вывести на экран).
encodedContent = fp.read()
decodedContent = encodedContent.decode("utf8")
# Выводим содержимое файла, полученного с сервера ('index.html').
print(decodedContent)
# Закрываем соединение с сервером.
fp.close()
Благодаря этому коду клиентское приложение может загрузить данные с сервера и вывести их на экран.
3c. Редактирование файла Dockerfile
Как и в случае с сервером, мы создаём для клиента простой Dockerfile
, ответственный за формирование среды, в которой будет работать клиентское Python-приложение. Вот код клиентского Dockerfile
:
# То же самое, что и в серверном Dockerfile.
FROM python:latest
# Импортируем 'client.py' в папку '/client/'.
ADD client.py /client/
# Устанавливаем в качестве рабочей директории '/client/'.
WORKDIR /client/
▍4. Docker Compose
Как вы могли заметить, мы создали два разных проекта: сервер и клиент. У каждого из них имеется собственный файл Dockerfile
. До сих пор всё происходящее не выходит за рамки основ работы с Docker. Теперь же мы приступаем к работе с Docker Compose. Для этого обратимся к файлу docker-compose.yml
, расположенному в корневой папке проекта.
Обратите внимание на то, что тут мы не стремимся рассмотреть абсолютно все команды, которые можно использовать в docker-compose.yml
. Наша главная цель — разобрать практический пример, дающий вам базовые знания по Docker Compose.
Вот код, который нужно поместить в файл docker-compose.yml
:
# Файл docker-compose должен начинаться с тега версии.
# Мы используем "3" так как это - самая свежая версия на момент написания этого кода.
version: "3"
# Следует учитывать, что docker-composes работает с сервисами.
# 1 сервис = 1 контейнер.
# Сервисом может быть клиент, сервер, сервер баз данных...
# Раздел, в котором будут описаны сервисы, начинается с 'services'.
services:
# Как уже было сказано, мы собираемся создать клиентское и серверное приложения.
# Это означает, что нам нужно два сервиса.
# Первый сервис (контейнер): сервер.
# Назвать его можно так, как нужно разработчику.
# Понятное название сервиса помогает определить его роль.
# Здесь мы, для именования соответствующего сервиса, используем ключевое слово 'server'.
server:
# Ключевое слово "build" позволяет задать
# путь к файлу Dockerfile, который нужно использовать для создания образа,
# который позволит запустить сервис.
# Здесь 'server/' соответствует пути к папке сервера,
# которая содержит соответствующий Dockerfile.
build: server/
# Команда, которую нужно запустить после создания образа.
# Следующая команда означает запуск "python ./server.py".
command: python ./server.py
# Вспомните о том, что в качестве порта в 'server/server.py' указан порт 1234.
# Если мы хотим обратиться к серверу с нашего компьютера (находясь за пределами контейнера),
# мы должны организовать перенаправление этого порта на порт компьютера.
# Сделать это нам поможет ключевое слово 'ports'.
# При его использовании применяется следующая конструкция: [порт компьютера]:[порт контейнера]
# В нашем случае нужно использовать порт компьютера 1234 и организовать его связь с портом
# 1234 контейнера (так как именно на этот порт сервер
# ожидает поступления запросов).
ports:
- 1234:1234
# Второй сервис (контейнер): клиент.
# Этот сервис назван 'client'.
client:
# Здесь 'client/ соответствует пути к папке, которая содержит
# файл Dockerfile для клиентской части системы.
build: client/
# Команда, которую нужно запустить после создания образа.
# Следующая команда означает запуск "python ./client.py".
command: python ./client.py
# Ключевое слово 'network_mode' используется для описания типа сети.
# Тут мы указываем то, что контейнер может обращаться к 'localhost' компьютера.
network_mode: host
# Ключевое слово 'depends_on' позволяет указывать, должен ли сервис,
# прежде чем запуститься, ждать, когда будут готовы к работе другие сервисы.
# Нам нужно, чтобы сервис 'client' дождался бы готовности к работе сервиса 'server'.
depends_on:
- server
▍5. Сборка проекта
После того, как в docker-compose.yml
внесены все необходимые инструкции, проект нужно собрать. Этот шаг нашей работы напоминает использование команды docker build
, но соответствующая команда имеет отношение к нескольким сервисам:
$ docker-compose build
▍6. Запуск проекта
Теперь, когда проект собран, пришло время его запустить. Этот шаг нашей работы соответствует шагу, на котором, при работе с отдельными контейнерами, выполняется команда docker run
:
$ docker-compose up
После выполнения этой команды в терминале должен появиться текст, загруженный клиентом с сервера: Docker-Compose is magic!
.
Как уже было сказано, сервер использует порт компьютера 1234
для обслуживания запросов клиента. Поэтому, если перейти в браузере по адресу http://localhost:1234/, в нём будет отображена страница с текстом Docker-Compose is magic!
.
Полезные команды
Рассмотрим некоторые команды, которые могут вам пригодиться при работе с Docker Compose.
Эта команда позволяет останавливать и удалять контейнеры и другие ресурсы, созданные командой docker-compose up
:
$ docker-compose down
Эта команда выводит журналы сервисов:
$ docker-compose logs -f [service name]
Например, в нашем проекте её можно использовать в таком виде: $ docker-compose logs -f [service name]
.
С помощью такой команды можно вывести список контейнеров:
$ docker-compose ps
Данная команда позволяет выполнить команду в выполняющемся контейнере:
$ docker-compose exec [service name] [command]
Например, она может выглядеть так: docker-compose exec server ls
.
Такая команда позволяет вывести список образов:
$ docker-compose images
Итоги
Мы рассмотрели основы работы с технологией Docker Compose, знание которых позволит вам пользоваться этой технологией и, при желании, приступить к её более глубокому изучению. Вот репозиторий с кодом проекта, который мы здесь рассматривали.
Уважаемые читатели! Пользуетесь ли вы Docker Compose в своих проектах?
Введение в Docker: зачем и как использовать контейнеры в любой системе
Если вы хоть немного прислушиваетесь к сообществу разработчиков программного обеспечения, вы наверняка слышали о Docker. Популярность этого сервиса в последнее время резко возросла, но он продолжает быстро привлекать пользователей, в том числе многие международные компании, инфраструктура которых зависит от него. Популярность Docker отчасти объясняется тем, что его пользователи мгновенно становятся поклонниками с евангельскими наклонностями.
Но что стоит за популярностью и как это работает? Давайте рассмотрим концептуальное введение, а затем изучим Docker, немного поигравшись с ним.
Что такое докер?
Docker позволяет запускать программное обеспечение в изолированной среде, называемой контейнером . Контейнер похож на виртуальную машину (ВМ), но работает совершенно по-другому (о чем мы скоро поговорим). Обеспечивая большую часть изоляции, которую выполняет виртуальная машина, контейнеры используют лишь часть ресурсов.
Почему это здорово
Прежде чем мы углубимся в технические детали, почему вас должно волновать это?
Последовательность
Допустим, вы пишете веб-приложение.Вы разрабатываете его на своей локальной машине, где и тестируете. Иногда вы запускаете его на промежуточном сервере, и вскоре вы собираетесь разместить его на большом производственном сервере, чтобы мир увидел его.
Разве не было бы замечательно, если бы вы могли последовательно , запускать одну и ту же среду на всех своих устройствах? Если ваше веб-приложение правильно работает внутри контейнера Docker на вашем локальном компьютере, оно запускается на вашем тестовом сервере, оно работает на производственном сервере, оно запускается где угодно.
Это делает управление зависимостями проекта невероятно простым.Не только просто иметь дело с внешними библиотеками и модулями, вызываемыми непосредственно вашим кодом, но и всю систему можно настроить по своему вкусу. Если у вас есть проект с открытым исходным кодом, который новый пользователь хочет загрузить и запустить, это так же просто, как запустить контейнер.
Воспроизводимая среда выигрывает не только в работающем коде — создание кода в контейнерах также является обычным делом; мы писали об использовании Docker для кросс-компиляции для Raspberry Pi.
Масштабируемость
Если вы используете Docker для создания служб, пользующихся разным спросом (например, веб-сайтов или API), невероятно легко масштабировать подготовку, просто запустив больше контейнеров (при условии, что все правильно спроектировано для этого).Существует ряд фреймворков для оркестровки кластеров контейнеров, таких как Kubernetes и Docker Swarm, но это уже история другого дня.
Как это работает
Контейнеры довольно умные. Виртуальная машина работает на смоделированном оборудовании и является полностью автономной ОС. Однако изначально контейнеры совместно используют ядро хоста. Вы можете увидеть различия ниже.
Это означает, что контейнеры работают ошеломляюще лучше, чем виртуальные машины.Когда мы говорим о Docker, мы говорим о том, сколько процессов Linux мы можем запустить, а не о том, сколько операционных систем мы можем поддерживать на плаву одновременно. В зависимости от того, что они делают, на вашем компьютере можно будет запускать сотни, если не тысячи контейнеров. Кроме того, запуск контейнера займет секунды или меньше, по сравнению с минутами для многих виртуальных машин. Поскольку контейнеры настолько легкие, обычной практикой является выполнение всех аспектов приложения в разных контейнерах, что обеспечивает исключительную удобство обслуживания и модульность.Например, у вас могут быть разные контейнеры для вашего сервера базы данных, redis, nginx и так далее.
Но если контейнеры совместно используют ядро хоста, то как они разделяются? Происходит несколько довольно изящных низкоуровневых уловок, но все, что вам нужно знать, это то, что пространства имен Linux активно используются, в результате чего получается полностью независимый контейнер со своими собственными сетевыми интерфейсами и многим другим. Однако барьер между контейнерами и хостами по-прежнему слабее, чем при использовании виртуальных машин, поэтому для критически важных с точки зрения безопасности приложений некоторые рекомендуют избегать контейнеров.
Как пользоваться?
В качестве примера мы собираемся создать минимальный веб-сервер в контейнере Docker. Для простоты мы будем использовать Flask, веб-микрофреймворк Python. Это программа, которую мы хотим запустить, main.py
:
из фляги импорт фляги app = Flask (__ имя__) @ app.route ('/') def home (): return 'Привет из контейнера Docker'; если __name__ == '__main__': приложение.запустить (хост = '0.0.0.0', порт = 80)
Не волнуйтесь, если вы не знакомы с Flask; все, что вам нужно знать, это то, что этот код обслуживает строку на локальном хосте : 80
.
Запуск процесса внутри контейнера
Так как же запустить это внутри контейнера? Контейнеры определяются изображением , которое похоже на рецепт контейнера. Контейнер — это просто запущенный экземпляр изображения (это означает, что у вас может быть несколько запущенных контейнеров одного и того же изображения).
Как получить изображение? Docker Hub — это общедоступная коллекция изображений, которая содержит официальные материалы, но также позволяет любому публиковать свои собственные. Мы можем взять образ из концентратора Docker и расширить его так, чтобы он делал то, что мы хотим. Для этого нам нужно написать Dockerfile
— список инструкций по сборке образа.
Первое, что мы сделаем в нашем Dockerfile
, это укажем, какой образ мы хотим использовать / расширить. В качестве отправной точки имеет смысл выбрать образ, в котором уже установлен Python.К счастью, есть хорошо поддерживаемый образ Python, который бывает во многих вариантах. Мы будем использовать его с Python 3.7, работающим на Debian stretch.
Вот наш Dockerfile
:
ОТ python: 3.7-растяжка КОПИРОВАТЬ приложение / / приложение WORKDIR / приложение RUN pip install Flask CMD ["питон", "main.py"]
После первой строки, которую мы обсуждали, остальная часть Dockerfile
говорит сама за себя. У нас есть такая иерархия:
приложение └── основная.ру Dockerfile
Итак, наш каталог app /
копируется в контейнер, и мы запускаем остальные команды, используя его в качестве рабочего каталога. Мы используем pip
для установки Flask
, прежде чем окончательно указать команду для запуска при запуске контейнера. Обратите внимание, что процесс, запускаемый этой командой, будет по своей сути привязан к контейнеру: если процесс завершится, контейнер умрет. Это (обычно) хорошо, если вы правильно используете Docker, когда внутри каждого контейнера выполняется только один процесс / фрагмент проекта.
Создание образа
Хорошо, мы написали несколько инструкций о том, как создать наш образ, так что давайте создадим его.
$ сборка докеров.
Это указывает Docker на поиск Dockerfile
в текущем каталоге (.
). Но чтобы упростить его запуск, давайте дадим нашему построенному образу имя, называемое тегом .
$ docker build -t flask-hello.
Поскольку мы впервые используем этот образ Python из хаба, загрузка займет минуту или две, но в будущем он будет доступен локально.
Теперь у нас есть блестящее новое изображение, которое мы можем запустить для создания контейнера! Это так просто:
$ запуск докера flask-hello
Наш контейнер работает успешно, и мы получаем вывод от Flask о том, что он готов к обслуживанию нашей страницы. Но если мы откроем браузер и зайдем на localhost
, ничего не будет видно. Причина, конечно же, в том, что наш сервер работает внутри контейнера, который имеет изолированный сетевой стек. Нам нужно перенаправить порт 80, чтобы иметь доступ к нашему серверу.Итак, давайте убьем контейнер с помощью CTRL + C и снова запустим его с переадресацией порта.
$ docker run -p 80:80 flask-hello
… и все работает!
Сводка
В первый раз, когда вы видите этот процесс, он может показаться немного запутанным. Но есть инструменты, такие как docker-compose
для автоматизации этого рабочего процесса, которые очень эффективны при запуске нескольких контейнеров / служб.
Откроем вам секрет: если вы познакомитесь с Docker, то почувствуете себя частью элитного клуба.Вы можете легко загрузить и запустить любое программное обеспечение, не требуя настройки, всего лишь несколькими нажатиями клавиш.
- Docker — это фантастический способ запуска кода в воспроизводимой кроссплатформенной среде.
- После настройки изображений вы можете мгновенно запускать код где угодно.
- Создание различных компонентов приложения в разных контейнерах — отличный способ упростить поддержку вашего приложения.
- Хотя в этой статье в качестве примера рассматриваются веб-сервисы, Docker может помочь в очень многих местах.
Контейнеры, оркестровка контейнеров и масштабируемое развертывание — захватывающие области, за которыми стоит наблюдать прямо сейчас, поскольку разработки происходят быстрыми темпами. Отличное время, чтобы повеселиться!
.
Docker Orchestration … Что это означает и зачем вам это нужно
[Эта статья написана Яроном Парасолом.]
Контейнеры Docker были созданы, чтобы помочь обеспечить быстрое и надежное развертывание компонентов или уровней приложений, создание контейнера, который содержит автономные части приложений, готовые к развертыванию, с промежуточным программным обеспечением и бизнес-логикой приложения, необходимыми для их успешного запуска. Например, приложение Spring в контейнере Tomcat. По замыслу Docker намеренно является изолированной автономной частью приложения, обычно это один уровень или даже один узел на уровне.
Однако приложение обычно имеет многоуровневую архитектуру, а это означает, что у вас есть уровни с зависимостями между ними, причем характер зависимости может быть любым, от сетевых подключений и удаленных вызовов API до обмена сообщениями между уровнями приложений. Следовательно, приложение представляет собой набор различных контейнеров с определенными конфигурациями. Вот почему вам нужен способ склеить части вашего приложения вместе.
Хотя у Docker есть базовое решение для соединения контейнеров с помощью моста Docker, это решение не всегда является предпочтительным, особенно при развертывании контейнера на разных хостах, и вам нужно позаботиться о реальных сетевых настройках.
Оркестровка Docker с TOSCA + Cloudify. Проверить это. Go
Итак, какую роль играет оркестратор?
Оркестратор позаботится о двух вещах:
Сроки создания контейнера — поскольку контейнеры должны быть созданы в порядке зависимостей и
Конфигурация контейнера, чтобы позволить контейнерам взаимодействовать друг с другом — и для этого оркестратору необходимо передавать свойства среды выполнения между контейнерами.
В качестве примечания здесь: с Docker вам понадобится специальная настройка, поскольку вы обычно не трогаете файлы конфигурации внутри контейнера, вы сохраняете контейнер нетронутым, поэтому есть интересный обходной путь для случаев, когда это требуется.
Один из способов сделать это — использовать план оркестрации на основе YAML для оркестровки развертывания приложений и процессов автоматизации после развертывания, что и используется Cloudify. Основанный на TOSCA (стандарт топологии и оркестрации облачных приложений), этот план оркестрации описывает компоненты и их жизненный цикл, а также отношения между компонентами, особенно когда речь идет о сложных топологиях.Это включает в себя то, что с чем связано, что на чем размещено и другие подобные факторы. TOSCA может описывать инфраструктуру, а также уровень промежуточного программного обеспечения и уровни приложений поверх них. Cloudify в основном берет этот план оркестровки TOSCA (дублированные чертежи на языке Cloudify) и материализует их, используя рабочие процессы, которые пересекают граф компонентов или этот план компонентов и выдают команды агентам. Затем они создают компоненты приложения и склеивают их вместе.
Агенты используют расширения, называемые подключаемыми модулями, которые являются адаптерами между конфигурацией Cloudify и различными API-интерфейсами инфраструктуры как услуги (IaaS) и инструментов автоматизации.
В нашем случае мы создали плагин для взаимодействия с Docker API.
Представляем подключаемый модуль Docker Cloudify
Плагин Cloudify-Docker довольно прост: он устанавливает конечную точку / сервер Docker API на машину, а затем использует привязку Docker-Py для создания, настройки и удаления контейнеров. События жизненного цикла TOSCA:
Create — установка компонентов приложения
Configure — настройка компонента
Start — запуск / запуск компонента
Еще есть stop & delete — для отключения и удаления
Мы начали с использования create — для создания контейнера мы не реализовали конфигурацию в начале и начали запускать приложение.Но потом мы поняли, что для контейнеров с зависимостями нам необходимо иметь свойства времени выполнения, такие как импорт IP контейнера-копии, например, для создания контейнера. Когда мы создаем контейнер сервера приложений, нам нужны порт и IP-адрес контейнера базы данных. Итак, мы подтолкнули создание контейнера к событию configure и использовали обработчик предварительной настройки отношений TOSCA, чтобы получить информацию о зависимом контейнере во время выполнения.
Способ предоставить информацию о времени выполнения контейнеру с зависимостями — это установить их как переменные среды.
01.
интерфейсы:
02.
cloudify.interfaces.lifecycle:
03.
настроить:
04.
реализация: docker.docker_plugin.tasks.configure
05.
входы:
06.
container_config:
07.
команда: mongod
--rest
--httpinterface --smallfiles
08.
образ: dockerfile / mongodb
09.
начало:
10.
реализация: docker.docker_plugin.tasks.run
11.
входы:
12.
container_start:
13.
port_bindings:
14.
27017
:
27017
15.
28017
:
28017
Пример Nodecellar
Я хотел бы объяснить, как это работает, на примере нашего приложения Nodecellar.Приложение Nodecellar состоит из двух хостов, которые в данном случае Cloudify не создавал, а просто подключил по SSH, а затем установил на них агентов. На одном у нас есть контейнер MongoD с процессом MongoD. С другой стороны, у нас есть контейнер Nodecellar с NodeJS и приложением Nodecellar внутри него. Контейнер Nodecellar требует подключения к контейнеру MongoD для выполнения запросов приложения при запуске приложения.
В конечном счете, оркестратор не должен ограничиваться развертыванием программного обеспечения, вся идея Docker заключается в обеспечении гибкости, поэтому мы также хотели бы использовать Docker в ситуациях автоматического масштабирования и автоматического восстановления, CD.В нашей следующей публикации мы покажем именно это — как Cloudify можно использовать с Docker для сценариев после развертывания.
.