Docker — Docker или контейнеризация всего и вся


Содержание

Введение в контейнеры, VM и Docker для новичков

Если вы являетесь программистом или техником, то вы наверняка хотя бы слышали о Docker — полезном инструменте для упаковки, отправки и запуска приложений в «контейнерах». Трудно не знать об этой вещи, ведь в последнее время она получает много внимания от всех, начиная от разработчиков и заканчивая сисадминами. Даже такие крупные шишки, как Google, VMware и Amazon создают сервисы, поддерживающие Docker.

Независимо от того, использовали ли вы уже Docker или нет, я думаю, важно понять некоторые фундаментальные концепции того, что такое «контейнер», и сопоставить его с виртуальной машиной (VM). В то время как Интернет полон отличных руководств по использованию Docker, я не смог найти понятных для начинающих руководств о концепции, особенно о том, из чего состоит контейнер. Итак, надеюсь, этот пост решит эту проблему!

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

Что такое «контейнеры» и «VM»?

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

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

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

Виртуальные машины

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

Давайте разберемся с жаргоном:

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

Виртуальная машина, работающая на хосте (опять же, с использованием гипервизора), также часто называется «гостевой машиной». Этот гостевой компьютер содержит как приложение, так и все, что ему нужно для его запуска (например, системные двоичные файлы и библиотеки). Он также включает в себя весь собственный виртуализированный аппаратный стек (в него входят виртуализированные сетевые адаптеры, хранилище и процессор). Это означает, что у него также есть своя полноценная гостевая операционная система. Изнутри гостевая машина ведет себя как отдельное устройство с собственными ресурсами. С точки зрения извне, мы знаем, что это VM-ресурсы совместного доступа, предоставляемые хост-машиной.

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

Во-первых, хостовый гипервизор виртуализации работает на операционной системе хост-машины. Например, компьютер с OSX может иметь виртуальную машину (например, VirtualBox или VMware Workstation 8), установленную «поверх» этой ОС. У VM нет прямого доступа к аппаратным средствам, поэтому она должна проходить через операционную систему хоста (в нашем случае — Mac OSX).

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

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

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

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

Как вы можете видеть на диаграмме, каждая виртуальная машина содержит виртуальное оборудование, ядро (т. е. ОС) и пользовательское пространство.

Контейнеры

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

С точки зрения всех целей и задач контейнеры ничем не отличаются от виртуальных машин. Например, они могут иметь личное пространство для обработки, выполнять команды, такие как root, иметь частный сетевой интерфейс и IP-адрес, разрешать кастомные маршруты и правила iptable, монтировать файловые системы и т. д.

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

На этой диаграмме показано, что контейнеры содержат только пользовательское пространство, а не ядро или виртуальное оборудование, как в случае с VM. Каждый контейнер Docker получает свое изолированное пользовательское пространство, позволяющее нескольким контейнерам запускаться на одном хосте. Мы видим, что вся архитектура ОС делится между контейнерами. С нуля создаются только файлы bin, lib. Именно поэтому контейнеры такие легкие.

Когда же в дело вступает Docker?

Docker — проект с открытым исходным кодом, основанный на контейнерах Linux. Он использует функции ядра Linux, такие как пространства имен и контрольные группы, для создания контейнеров поверх ОС.

Контейнеры — это не новое изобретение. Google использует собственные технологии контейнеров уже многие годы. Другие технологии контейнеров на Linux включают в себя Solaris Zones, BSD jails и LXC, которые существуют уже много лет.

Так почему же Docker внезапно набирает обороты?

  1. Простота использования: Docker упростил для всех (разработчиков, системных администраторов, архитекторов и других) использование контейнеров для быстрой сборки и тестирования портативных приложений. Это позволяет кому угодно передавать приложение на свой ноутбук. Такое приложение, в свою очередь, может работать без изменений в любом общедоступном облаке, в частном облаке или даже на чистом сервере. Девиз таков: «Создайте один раз, запускайте откуда угодно»;
  2. Скорость: Контейнеры в Docker очень легкие и быстрые. Поскольку контейнеры — это помещенные в «песочницу» среды, работающие на ядре, они потребляют меньше ресурсов. На создание и запуск Docker контейнеров тратятся мгновения по сравнению с VM. Работа с ними может занять больше времени, потому что каждый раз они должны загружать полную виртуальную ОС;
  3. Docker Hub: Пользователи Docker также получают выгоду от все больше богатеющей экосистемы Docker Hub, которую вы можете назвать «магазином приложений для образов Docker». Docker Hub содержит десятки тысяч общедоступных образов, созданных сообществом. Они легко доступны для использования. Теперь невероятно легко найти образы, которые отвечают вашим потребностям. Их уже можно взять и использовать практически без изменений;
  4. Модульность и масштабируемость: Docker позволяет легко разбить функциональность вашего приложения на отдельные контейнеры. Например, ваша база данных Postgres может работать в одном контейнере, а сервер Redis — в другом, приложение Node.js — в третьем. С Docker стало проще связать эти контейнеры вместе для создания приложения. В будущем это упростит масштабирование или обновление компонентов независимо друг от друга.

И наконец: кто же не любит кита Docker?

Фундаментальные Docker концепции

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

Docker Engine

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

  • Docker Daemon (работает на хосте);
  • Docker Client (связывается с Docker Daemon, чтобы осуществлять команды);
  • REST API (для удаленного взаимодействия с Docker Daemon).

Docker Client

Клиент Docker — то, с чем вы, конечный пользователь Docker, взаимодействуете. Вы можете представить его как интерфейс Docker. Например, когда вы задаете:

Вы связываетесь с клиентом Docker, который впоследствии передает ваши инструкции Docker Daemon.

Docker Daemon

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

Dockerfile

Dockerfile — то, где вы пишете инструкции для создания образов Docker. Эти инструкции могут быть таковы:

  • Для установки пакета ПО: RUN apt-get y install some-package;
  • Для раскрытия порта: EXPOSE 8000;
  • Для передачи переменной среды: ENV ANT_HOME /usr/local/apache-ant.

Бывают и другие инструкции. Как только вы запустите Dockerfile, то сможете использовать команду docker build для создания образа. Вот пример Dockerfile:

Docker Image

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

Образы Docker создаются с помощью Dockerfile. Каждая инструкция в Dockerfile добавляет к образу новый «слой». Слои представляют собой часть файловой системы, которая либо дополняет слой под собой, либо заменяет его. Слои — ключ к легкой, но мощной структуре Docker. Для этого Docker применяет Union FS.

Union File System

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

Содержимое директорий, которые имеют одинаковые пути в ветках, представляется в виде объединенного директория. Из-за этого не требуется создавать копии каждого слоя. Вместо этого они получат указатели к одному и тому же ресурсу. Когда вам понадобится изменить определенные слои, будет создана копия. Изменения будут происходить на копии, а оригинал останется нетронутым. Поэтому вам может казаться, что файловые системы можно переписать, хотя на самом деле они этого не позволяют. (Другими словами, это система «копирование при записи».)

Многоуровневые системы имеют 2 главных преимущества:

  • Свободное копирование: слои помогают избежать необходимости дублировать полный набора файлов каждый раз, когда вы используете образ для создания и запуска нового контейнера. Это делает создание контейнеров в Docker очень быстрым и «дешевым»;
  • Разделение слоев: внесение изменений происходит намного быстрее — при изменении образа Docker распространяет обновления только на слой, который был изменен.

Volume

Volume — это часть контейнера, ответственная за данные. Ее инициализация происходит при создании контейнера. Volume позволяют хранить и передавать данные контейнера. Volume отделены от обычной Union FS. Они существуют в виде обычных директорий и файлов в хостовой системе файлов. Даже если вы разрушите, обновите или рекомпилируете контейнер Docker, данные Volume останутся нетронутыми. Если вы хотите обновить Volume, нужно изменять его напрямую. (Дополнительное преимущество: данные в Volume можно распределять и использовать в нескольких контейнерах, что достаточно удобно.)

Контейнеры Docker

Контейнер Docker, как говорилось выше, упаковывает ПО приложения в невидимую «коробку», в которой есть все, что необходимо для запуска приложения. Туда входят ОС, код приложения, среда, инструменты, библиотеки и т. д. Контейнеры Docker создаются из образов. Так как образы доступны только для чтения, для создания контейнера Docker добавляет файловую систему «чтение/запись» поверх системы «read-only».

Более того, после создания контейнера Docker добавляет сетевой интерфейс (он позволяет контейнеру общаться с локальным хостом), присоединяет доступный IP-адрес к контейнеру и выполняет процесс, который вы указали для запуска приложения при определении образа.

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

Применение двойного клика к контейнерам


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

Термин «контейнер» на самом деле является просто абстрактным понятием, описывающим, как несколько различных функций работают вместе для визуализации «контейнера». Давайте очень быстро пробежимся по ним:

1) Пространства имен

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

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

a. NET: Предоставляет контейнер с собственным представлением сетевого стека системы (например, собственные сетевые устройства, IP-адреса, таблицы IP-маршрутизации, каталог /proc/net, номера портов и т. д.);

b. P >ps aux в командной строке, чтобы проверить, какие процессы запущены в вашей системе, вы видели столбец с названием «PID». Пространство имен PID дает контейнерам свое собственное видение процессов, которые они могут просматривать и с которыми способны взаимодействовать, включая независимый init (PID 1), который является «предком всех процессов»;

c. MNT: дает контейнеру собственное представление о «монтировании» в системе. Таким образом, процессы в разных пространствах имеют разные представления об иерархии файловой системы;

d. UTS: UTS обозначает UNIX Timesharing System. Это позволяет процессу идентифицировать системные идентификаторы (т. е. имя хоста, имя домена и т. д.). UTS позволяет контейнерам иметь свое собственное имя хоста и имя домена NIS, которое не зависит от других контейнеров и хост-системы;

e. IPC: IPC расшифровывается как InterProcess Communication. Пространство имен IPC отвечает за изоляцию ресурсов IPC между процессами, выполняющимися внутри каждого контейнера;

f. USER: это пространство имен используется для изоляции пользователей в каждом контейнере. Он функционирует, позволяя контейнерам иметь другое представление диапазонов uid (идентификатор пользователя) и gid (идентификатор группы) по сравнению с хост-системой. В результате uid и gid процесса могут различаться внутри и снаружи пространства имен пользователя. Это позволяет процессу иметь непривилегированного пользователя вне контейнера, не жертвуя привилегиями root внутри него.

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

2) Контрольные группы

Контрольные группы (также называются cgroups) — это функция ядра Linux, которая изолирует, расставляет приоритеты и учитывает использование ресурсов (ЦП, память, дисковый ввод-вывод, сеть и т. д.) для набора процессов. Cgroup гарантирует, что контейнеры Docker используют только те ресурсы, которые им необходимы, и при необходимости устанавливает ограничения на то, какие ресурсы контейнер может использовать. Cgroups также гарантирует, что единственный контейнер не исчерпает один из этих ресурсов и не разрушит всю систему.

Наконец, Docker также использует Union FS.

3) Изолированная Union FS

Описана выше в разделе «Docker Image».

Вот и все, что существует в контейнере Docker. Конечно, все трудности начинаются в деталях осуществления: например, как управлять взаимодействиями между различными компонентами?

Будущее Docker: сосуществование Docker и VM

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

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

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

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

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

Заключение

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

4gophers

Делаем свой контейнер в 100 строчек кода

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

В этой серии статей мы поговорим о технологиях, которые лежат в основе контейнеров, что сейчас пользуется популярностью у разработчиков, рассмотрим частые проблемы, возникающие при развертывании контейнеров(например, интеграция с CI и СD) и поговорим о мониторинге при переменной нагрузке и частой смене потенциалов. В заключении мы помечтаем о будущем контейнеризации и обсудим, какой популярностью пользуется unikernels в самых передовых организациях.

Эта статья одна из серии «Containers in the Real World — Stepping Off the Hype Curve». Вы можете подписаться на обновления через RSS.

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

Чтобы реально понимать что такое контейнеры и какое место они занимают в мире разработки ПО, необходимо понимать как они работают. И в этой статье мы как раз об этом поговорим. Узнаем отличия между контейнерами и контейнеризацией, обсудим контейнеры в Linux (включая namespace, cgroup и слоенные файловые системы). Напишем немного кода, чтобы создать простой контейнер с нуля, и, наконец, сделаем выводы и посмотрим куда нас все это приведет.

Что такое контейнеры на самом деле?

Я люблю играть в игры. Давайте сыграем в одну прямо сейчас. Представте, что вам нужно ответить на вопрос «что такое контейнер?». Ответили? Попытаюсь угадать ваши ответы:

  • Способ совместного использования ресурсов.
  • Изоляция процессов.
  • Один из способов легкой виртуализации.
  • Упаковка корневой файловой системы и метаданных вместе.
  • Своеобразная chroot тюрьма.
  • Что-то вроде транспортного контейнера.
  • Все что делает докер.

Посмотрите, одно слово означает довольно много вещей. Так сложилось, что слово «контейнер» стали использовать для обозначения большого количества различных концепций. Оно используется как для определения аналогии с контейерезацией, так и для определения технологий для реализации контейнеров. Если рассматривать их по отдельности, то все становится прозрачнее. И так, давайте поговорим, зачем нам нужны контейнеры. А затем узнаем, как нам этого добиться(и потом снова вернемся к первому вопросу).

В начале

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

Но простой запуск run.sh не возможен без наличия всех необходимых ему зависимостей. Нужны определенные библиотеки, установленные на этом хосте. Кроме того, программы, как правило, не хотят работать одинаково на локальном хосте и на удаленном(и не пробуйте меня переубеждать). В результате мы изобрели AMI (Amazon Machine Images), и VMware образы, Vagrantfile, и многое другое. И все было хорошо.

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

И все опять стало хорошо.

Кеширование — это именно то, что очень выгодно отличает Docker контейнеры от VMDK или Vagrantfile. Теперь мы можем работать только с дельтами базовых образов, а не с полными огромными образами. Это означает, что мы можем запаковать рабочую среду и перенести ее на другую машину. Вот почему, когда вы запускаете docker run whatever , то Docker поднимается очень быстро, даже если это выглядит как запуск нового образа системы. Мы поговорим о том, как это работает чуть позже.

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

Создание контейнеров

И так, что такое контейнер в более практическом смысле? Бы ло бы очень замечательно, если контейнер создавался с помощью системного вызова create_container . Но, к сожалению, это не так. Хотя, если откровенно, то не многим сложнее.

Чтобы говорить о контейнерах на более низком уровне, нам нужно разбираться в трех вещах: пространствах имен(namespaces), cgroups, и слоенных файловых системах(layered filesystems). Конечно, нужно еще много всего, но именно эти три вещи делают всю магию.

Пространства имен

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

Пространства имен бывают:

  • PID: Это пространство имен предоставляет процессу и его потомкам собственное видение набора процессов в системе. Это можно представить себе как таблицу соответствия. Когда процесс в пространстве PID запрашивает у системы список процессов, то ядро сначало смотрит в эту таблицу соответствия. Если процесс существует в этой таблице, то используется соответствующий ID вместо реального ID. Если же процесса нет в таблице, то ядро делает вид, что процесса вообще не существует. Пространство PID задает первому процессу ID 1, таким образом, мы можем изолировать дерево процессов для каждого контейнера.
  • MNT: Это одно из самых важных пространств имен. Оно предоставляет процессу отдельные таблицы монтирования. Это значит, что процесс может монтировать и отмонтировать необходимые директории не затрагивая других пространств имен(в том числе и основное). И еще один важный момент: в сочетании с использованием системного вызова pivot_root мы можем позволить процессу иметь свою собственную файловую систему. Именно благодаря этой фиче, просто переключая файловые системы, которые видит контейнер, мы можем сделать вид, что процесс запущен на ubuntu, busybox или alpine.
  • NET: Сетевое пространство имен предоставляет процессу возможность использовать свой собственный сетевой стек. Конечно, только процессы из главного сетевого пространства имен (те, которые запускаются, когда вы включаете компьютер) имеют доступ к реальным сетевым картам. Но мы можем создавать виртуальные изернет пары — виртуально связанные сетевые карты в разных пространствах имен. Это выглядит как наличие нескольких ip стеков на одной машине, которые могут общаться друг с другом. Если добавить немного магии роутинга, то можно предоставить контейнерам доступ в реальный мир, несмотря на изоляцию в своем собственно ip стеке.
  • UTS: Пространство имен предоставляет процессу возможность иметь свои собственные имена для хоста и домена. После настройки пространства имен UTS, любые изменения имени хоста или домена не затронут другие процессы.
  • IPC: Это пространство имен изолирует механизм межпроцессорного взаимодействия, таких как очередь сообщений. Для более глубокого понимания, загляните в документацию.
  • USER: Это пространство имен было добавленно совсем недавно и именно настройка этого пространства оказывает наибольшее влияние на безопасность работы контейнера. Пространство USER выполняет мапинг между UID’ами в рамках процесса к UID’ами (и GID’ами) самого хоста. Это очень полезно. Используя это пространство имен, мы можем привязать ID пользователя root в контейнере (например 0) к произвольному и непривилегированному UID на реальном хосте. Таким образом, мы можем предоставить root доступ к ресурсам внутри контейнера, но, на самом деле, не предоставляя root права в рамках всего хоста. Контейнер может запускать процессы с UID 0 (что означает root пользователя) но в ядре будет происходить мапинг этого UID с некоторым непривилегированном реальным UID. Большинство систем контейнеризации не мапят UID внутри контейнера на нулевой UID в вызывающем пространстве.
Цукерберг рекомендует:  C# - Функции в c#

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

Cgroups

Cgroups это тема для отдельной статьи и не одной (думаю, я когда ни будь этим займусь). В рамках этого туториала я опишу их совсем кратко. Если есть желание или проблемы с пониманием основной концепции, то всегда можете обратиться к официальной документации.

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

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

Слоенный файловые системы

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

На базовом уровне, слоенные файловые системы оптимизируют создание копий основных файловых систем для каждого контейнера. Это можно сделать различными способами. Btrfs использует копирование при записи на уровне файловой системы. Aufs использует «объединенное монтирование». Способов действительно очень много. В рамках этой статьи мы будем использовать самый простой: просто делать копию. Это очень медленный способ но он работает и прост для понимания.

Создание контейнера по шагам

Шаг первый. Создаем шаблон приложения


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

Что делает этот код? Мы читаем первый аргумент, если это строка «run» , то запускается метод parent() , а если это строка «child» , то запускаем метод child() . В методе parent() запускается специальный файл /proc/self/exe , который содержит образ памяти для текущего исполняемого файла. Другими словами, мы заново запускаем себя же, но с указанием параметра «child» .

Что это за сумашествие? Ну, на самом деле, это еще не очень большое сумашествие. Просто, теперь мы можем запускать другу программу, которая, в свою очередь, запускает программу указанную пользователем(параметр os.Args[2:] ). Теперь у нас есть каркас, перейдем к созданию контейнера.

Шаг второй. Используем пространства имен

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

Теперь ваша программа будет запускаться внутри пространств имен UTS, PID и MNT.

Шаг третий. Корневая файловая система.

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

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

Две последние строчки самые важные, они сообщают файловой системе, сменить текущую директорию с / на rootfs/oldrootfs , и переключить новую rootfs директорию на / . После вызова pivotroot , директория / внутри контейнера указывает на rootfs (биндинг при монтировании необходим, чтобы удовлетворить требованиям команды pivotroot — OS требует использовать pivotroot для переключения файловых систем, котрые не являются частями одного дерева)

Шаг четвертый. Инициализация мира контейнера

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

И мы пропустили этап настройки самого контейнера. Сейчас у нас есть сырой контейнер, запущенный с использованием изолированных пространствах имен. Мы настроили MNT пространство, но все остальное осталось по умолчанию. В реальной жизни, нам нужно было бы настроить «мир» контейнера перед запуском пользовательского процесса. К примеру, мы могли бы настроить сеть, переключится на необходимый UID до запуска процесса, установить необходимые лимиты (например, rlimits) и так далее. Но это уже больше чем 100 строчек кода.

Шаг пятый. Собираем все вместе

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

К чему все это?

Тут я буду немного противоречивым. Для меня, контейнеры это способ доставки кода куда угодно и инструмент для дешевого(в плане ресурсов) изолированного запуска процесса, но это еще не конец истории. Контейнеры — это набор технологий, а не пользовательский опыт и умения.

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

Платформы как Сервис(PaaS), такие как Cloud Foundry, построенные поверх контейнеров, начинали именно с управления кодом, а не с контейнеризации. Большинства разработчиков просто хочет запустить свое приложение нажатием одной кнопки. За кулисами таких сервисов происходит много всего: запускаются контейнеры, как-то настраиваются и т.д. В случае с Cloud Foundry вы можете вообще пропустить этот шаг и использовать подготовленные Docker файлы.

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

Экосистема Docker: основы контейнеризации

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

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

Данная статья поможет разобраться в основах контейнеризации и понять преимущества такого подхода. Docker позволяет легко и быстро масштабировать и управлять распределёнными контейнерами.

Краткая история контейнеризации Linux

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

В Linux основой для развития контейнеризации является система LXC, добавленная в ядро в 2008 году. Для обеспечения изоляции процессов система LXC скомбинировала cgroups (позволяет изолировать и отслеживать использование ресурсов) и пространства имён (разделяют группы таким образом, что они не пересекаются друг с другом).

Позже был представлен инструмент Docker, предназначенный для упрощения процессов создания и управления контейнерами. Система Docker изначально использовала LXC в качестве драйвера по умолчанию (для этого была разработана библиотека libcontainer). На момент своего появления Docker не предлагал большого количества инновационных идей, однако благодаря стандартизации интерфейса и упрощению процессов контейнеризации эта технология стала доступной для обычных системных администраторов и разработчиков.

Преимущества контейнеризации

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

Разделение хост-системы и контейнеризованного приложения

Контейнеры несут в себе полную стандартизацию. Это значит, что контейнер подключается к хосту и ко внешним сервисам с помощью заранее определенных интерфейсов. Контейнеризованные приложения не должны каким-либо образом зависеть от ресурсов или архитектуры хост-системы. Это упрощает требования, предъявляемые к операционному окружению при разработке приложения. Аналогично, хост воспринимает контейнеры как «чёрные ящики», никак не влияющие на его работу (вне зависимости от того, какое приложение помещено в контейнер).

Высокая масштабируемость

Данное преимущество тесно связано с предыдущим. Благодаря разделению хост-системы и контейнеров компоненты приложения легко масштабируются. Сервис-ориентированная архитектура (речь о которой пойдёт далее) вместе с контейнеризованными приложениями – надёжная основа для быстрого масштабирования.

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

Простое управление зависимостями и версиями приложения

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

Это значительно упрощает управление зависимостями и версиями приложения. Хост больше не несёт ответственности за зависимости, поскольку всё необходимое уже содержится в самом контейнере (за исключением тех случаев, когда один контейнер зависит от другого).

Легковесная изолированная среда выполнения

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

Совместно используемые слои

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

Компонуемость и предсказуемость

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

Использование Docker-файлов

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

Примечание: Docker-файл – это разновидность build-файла, в котором хранится описание процесса создания образов.

Docker-файлы очень удобны, полезны и просты в написании. Основные их преимущества:

  • Простое управление версиями: Docker-файлы можно сохранять в системе контроля версий для отслеживания изменений и отмены ошибок.
  • Предсказуемость: Сборка образов с помощью Docker-файла устраняет ошибки пользователя в процессе создания образов.
  • Управление: Чтобы поделиться созданным образом, пользователь обычно предоставляет Docker-файл, с помощью которого можно создать данный образ; тогда другие пользователи могут проконтролировать процесс создания образа. По сути, выкладывая Docker-файл в открытый доступ, вы предоставляете историю действий, выполненных для построения образа.
  • Гибкость: Создавая образы из Docker-файла, вы можете переопределить настройки по умолчанию в интерактивном режиме.

Архитектура контейнеризованных приложений

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

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

Спроектированные таким образом приложения должны обладать следующими характеристиками:

  • Они не должны учитывать особенности хост-системы или полагаться на них.
  • Каждый компонент должен иметь API, с помощью которого пользователи могут получить доступ к сервису.
  • При начальной настройке каждый сервис должен принимать во внимание переменные окружения.
  • Приложения должны хранить свои данные вне контейнера (в смонтированных томах или отдельных специальных контейнерах).

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

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

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

Управление контейнерами с помощью реестров Docker

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

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

Заключение


Docker – надёжная основа для распределённого развёртывания контейнеров. Благодаря возможности упаковывать компоненты приложения в отдельные контейнеры горизонтальное масштабирование становится довольно простым процессом. Docker предоставляет огромное множество инструментов для создания контейнеров, для управления ими и совместного их использования с другими пользователями или хостами.

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

Docker: контейнеры — это просто

Что такое Docker

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

Docker был написан на языке программирования Go и выпущен в 2013 году. Изначально он работал только с Linux-системами, однако на данный момент его можно использовать также в Windows и macOS. Несмотря на то, что проект является относительно новым, он уже широко используется многими специалистами и продолжает завоевывать популярность.

Важной частью экосистемы Docker является Docker Hub — открытый репозиторий образов контейнеров. В нём можно найти десятки готовых приложений от официальных разработчиков. Среди них — nginx, MySQL, Apache, Gitlab, Redmine, Elasticsearch, Jenkins и другие.

Например, для запуска WordPress с помощью Docker достаточно выполнить следующие команды:

После этого откройте в браузере страницу http://12.34.56.78 (здесь укажите реальный IP-адрес вашего VDS) и приступите к настройке CMS!

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

  • образ (image) – шаблон, который используется для создания контейнеров. Представляет собой слепок файловой системы, в котором расположен код приложения и его окружение;
  • реестр (registry) – репозиторий образов. Docker Hub, о котором шла речь выше, — это публичный репозиторий, где хранится огромное количество образов;
  • контейнер (container) – запущенное приложение, т.е. совокупность процессов и образа.

Преимущества Docker

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

Возьмем ситуацию, когда вам нужно установить Redmine, Github, Jenkins или что-либо другое. В обычной ситуации вы бы самостоятельно устанавливали пакеты, настраивали окружение, а также отдельно устанавливали бы веб-сервер. С Docker все намного проще – вам просто необходимо запустить готовый контейнер из репозитория hub.docker.com. В базе Docker содержится более 100 тысяч готовых к установке приложений, среди которых вы точно найдете нужное.

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

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

Docker в VDS Timeweb

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

Для того, чтобы начать использовать Docker, сначала вам необходимо установить его – это можно сделать двумя путями.

  1. Вы можете установить Docker в панели управления VDS при создании нового сервера: на 2-м шаге «Программное обеспечение» при выборе Ubuntu 16.04 в качестве операционной системы вы сможете установить DockerUI.
  2. Также вы можете провести установку через консоль любого дистрибутива Linux, следуя инструкции на официальном сайте: https://docs.docker.com/engine/installation/linux/

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

Как и во многих других случаях, работа с Docker напрямую через консоль отличается большей стабильностью и более широкими возможностями. Все необходимые команды вы найдете на этой странице официального сайта: https://docs.docker.com/engine/reference/commandline/

Однако начинающим пользователям, которые хотят познакомиться с Docker, будет удобнее работать в DockerUI:

Docker: контейнеризация старых приложений

Стоит ли пытаться упаковывать старые приложения в контейнеры? Можно ли это делать эффективно?
Ответ на оба этих вопроса однозначно, да! И вот почему.

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

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

Зачем вообще запускать приложение в контейнерах?

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

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

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

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

Два подхода к контейнеризации вашего старого приложения

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

“Чистая” стратегия контейнеризации унаследованного приложения

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

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

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

  • Разбиение приложения по функциям или модулям. Если в вашем приложении уже четко определенны наборы функций или явно выделены определенные модули, например, «работа с корзиной покупателя», вы можете использовать их в качестве основы для рефакторинга. Каждый микросервис может состоять из отдельной функций или их тесно связанных групп (или модулей).
  • Разбиение приложения по ресурсам. Каждый микросервис может быть определен на основе конкретного ресурса, за который он ответственен в конечном приложении, например, микросервис доступа к базе данных, микросервис, занимающийся передачей сообщений между отдельными приложениями, микросервис, взаимодействующий с периферийными устройствами. и т.д.
  • Разбиение приложения по границам существующих компонентов. Этот путь может быть полезен, когда в архитектуре унаследованного приложения существуют определенные компоненты с явными границами между собой, а данные между этими компонентами перемещаются по ограниченному количеству каналов.

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

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

“Грязный” подход к контейнеризации

«Грязный» подход к упаковке устаревшего приложения состоит в основном в том, чтобы взять это приложение целиком и поместить его в контейнер. Приложение, упакованное таким образом может использовать лишь некоторые функции контейнеров (т.к. сетевые и системные ресурсы). Однако, без полного редизайна ваше приложение не будет являться микросервисным.

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

Цукерберг рекомендует:  JavaScript. Уровень 1. Интерактивные веб-приложения

Истина где-то посередине

Само собой, какой именно способ упаковки устаревших приложениями использовать («чистый» или «грязный»), не всегда может быть очевидно. Иногда легче реорганизовывать приложение частично, т.е. постепенно перемещать некоторые функции из приложения в отдельные микросервисы, держа основное приложение в отдельном контейнере. Когда контейнеризация происходит именно таким образом, промежуточный результат точно не будет элегантным с точки зрения стандартов контейнеризации или микросервисной архитектуры, но тем не менее позволит вам существенно оптимизировать во времени затраты на рефакторинг вашего приложения.

А иногда вам просто не нужна контейнеризация

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

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

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

Зачем нам Docker?

Многие интересуются технологией, которая обещает изменить подход к организации жизненного цикла приложений под названием Docker. Если начинать с определений, то оно будет звучать как-то вроде: “Docker — это технология контейнеризации приложений…” и далее по тексту. Но я хотел бы прерваться на этом месте и сделать важное отступление. Дело в том, что с технологической точки зрения Докер — далеко не прорывной проект, это один из представителей целого семества проектов, реализующих технологию контейнеризации. Более того, Докер — не первый среди них, и чтобы уж совсем развеять ореол исключительности — все они опираются на примерно один и тот же стек базовых технологий.

Так почему же Докер у всех на слуху, а про OpenVZ или LXC мало кто слышал? Почему Докер один из всего семейства схожих проектов выстрелил, а его родственники — в лучшем случае остались нишевыми инструментами? Все дело не в технологическом превосходстве, а в правильной идее и позиционировании. Команда Докера взялась решать проблему, которую до них не решали другие проекты контейнеризации.

Минутка истории

Давайте проследим современную историю программостроения (концентрируясь на серверном ПО) и самостоятельно дойдем до тех проблем, решением которых и позиционирует себя Докер.

Зависимости

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

С одной стороны это великолепно: резко сокращается время на разработку приложений, проект любого приложения завершен на 97% еще до написания первой строчки кода. С другой стороны — оставшиеся 3% кода, которые мы напишем сами, предъявляют большой набор требований к окружению, в котором они будут запускаться и работать:

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

Звучит как дохрена работы. Но все не было бы так страшно, если бы история заканчивалась на ноутбуке разработчика, который один раз как-то умудрился все настроить и больше ничего не трогает.


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

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

Последним гвоздем в крышку нашего гроба может стать ситуация, когда мы захотим на одном сервере разместить несколько приложений со взаимоисключающими зависимостями: приложение А хочет рантайм языка версии 3, а приложение Б — версии 5, которые друг с другом не совместимы. Все? Приплыли?

Изоляция

Раз интернеты еще не развалились — то значит какое-то решение умные люди придумали, правда? Этим решением были различные технологии изоляции. Если у нас имеется “проблема коммуналки”, когда логически не связанные друг с другом приложения делят единое окружение, как чуждые друг другу жильцы коммуналки делят санузел и кухню, то надо эту коммуналку расселить.

Виртуализация

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

Это позволит нам установить на виртуальную машину операционную систему по вкусу, а за ней и весь комплект зависимостей нашего приложения. Нам не нужно ломать голову над тем, как тут будут уживаться несколько приложений, мы выделим каждому по своей виртуальной машине. Ура! Коммуналочки расселены! Победа? Или пока только одной проблемой меньше?

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

Контейнеризация

Не пожелав мириться с таким раскладом умные люди подумали-подумали и поняли, что почти никогда им на самом деле не нужно эмулировать оборудование и ставить отдельную ОС. Это лишь средство достижения цели, которая на самом деле звучит как “приложению должно казаться, что оно одно на этом компьютере”.

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

  • файловую систему
  • память с деревом процессов
  • сетевую активность

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

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

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

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

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

Ну что? На этот раз победа? К сожалению, еще нет. Если мы остановимся здесь, то получим те самые известные только в узких кругах технологии контейнеризации, появившиеся до Докера. А все потому, что кроме “проблемы коммуналочек” существует еще как минимум одна проблема, которую необходимо решить.

Mutable Monster

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

Представим себе ситуацию, когда мы хотим установить программу X. В инструкции написано, что у нее есть зависимости A и B. Для наглядности пусть все будет слегка игрушечное: пусть X, A и B — просто файлы, которые должны оказаться в одной папочке, чтобы все заработало. Также у нас есть свой примитивный язык для скриптов установки:

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

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

Файл A на самом деле не нужен, а чтоб все завелось — надо подкинуть файлы B и С.

Мы правим наш скрипт в соответствии с советом:

Запускаем, и… работает! Отписываемся под советом, обещаем Васяну проставиться при случае и коммитим наш скрипт… Только чтобы утром услышать от коллег, что он не работает. После стандартных аргументов про кривые руки мы проверяем скрипт на случайном чистом сервере — и правда не работает! Как так?!

Дело в том, что в любой системе, в которую можно вносить изменения (Mutable) успешной является не последняя попытка, а сумма всех попыток. Если мы долго и так и этак пинали систему и в конечном итоге оно завелось, то это не значит, что последний вариант был правильный и надо тут же его документировать. Он просто нужным образом наслоился на все неправильные варианты, которые хотя бы частично продвигали нас в нужном направлении. Совет Васяна был отчасти верным, после долгих разборок мы выяснили, что программа X зависит от всех трех файлов: A, B и C. И правильный скрипт установки выглядит следующим образом:

Почему у нас все заработало и нам показалось, что скрипт верный? Мы добавили файл A во время первой, неудачной попытки. И “как-бы верного” второго варианта скрипта хватило, чтобы довести систему до ума.

На таком тривиальном примере, где у нас:

  • одно единственное приложение
  • все зависимости представлены в виде одного файлика
  • присутствует только операция добавления файла

мы умудрились все поломать, запутаться в трех елках и повыяснять отношения с коллегами. В реальном же мире:

  • приложение может состоять из многих компонентов
  • зависимости представляют собой сложные объекты, которыми манипулируют пакетные менеджеры
  • присутствуют операции удаления, изменения, версионирования, взаимные зависимости и конфликты

Таким образом ситуация многократно усложняется и выходит из под контроля. Вот тут-то и вылезает этот самый Mutable Monster — сущность, на которую от начала времен наложили такое количество изменений, что никто уже не может точно описать её текущее состояние. Даже если мы старательно документировали каждое изменение (в журнале строгого учета с подписями и печатью :)), то не факт, что среди этих накатов-откатов-перекатов мы сможем найти рациональное зерно и кратчайший путь от “чистой” системы до “рабочей”.

Житейский пример: думаю всем знаком ритуал переустановки Windows. Со временем система “замусоривается” и “замедляется”. Мы вынуждены прибегать к таким житейским терминам из-за невозможности точно описать причину возникающих проблем. Куча программ и установщиков могут вносить изменения в системную коммуналочку (совокуность двух проблем, которые я расписывал до этого момента). Даже если каждый такой установщик честно и точно зафикисирует все свои действия, то со временем распутать этот клубок человеческими усилиями становится неоправдано сложно, начать с чистого листа проще, чем заниматься микро-хирургией. Кто-то может сказать, что переустановками занимаются только лузеры и эникейщики, не способные разобраться в проблеме, но на эту ситуацию можно посмотреть и с другой стороны.

Почему принтер так хорош в печати одинаковых копий одной и той же страницы? “Ну это же машина! Арифметика и копирование — две задачи, с которыми компьютеры справляются лучше всего!” — скажите вы и будете правы. Но есть и еще один аспект успеха: принтер всегда начинает с чистого листа. Это настолько хороший подход, что даже само выражение выражение “начать с чистого листа” стало крылатым. Зная, что у нас всегда одинаковые начальные условия, мы можем здорово упростить механизм принтера, увеличив его производительность и надежность.

Представьте, что мы поставили себе задачу создать принтер, который сможет печатать на использованных листах. В принтер можно положить лист, на котором уже что-то напечатано, а он бы вносил в имеющийся текст правки, как учитель красной ручкой в тетрадке, но в намного меньшем масштабе. Устройство принтера многократно усложнится, а результат будет не уровня идеальных копий, а “как шмогла”.

А ведь это именно то, чем мы занимаемся в создании окружений для наших приложений. Мы пытаемся привести окружение из неизвестного состояния в желаемое. Эта стратегия обречена на поражение. Это не вопрос “насколько легко вы это делаете?” а “сколько вы так продержитесь?”.

Docker to the rescue!

В попытке решить сразу обе эти проблемы (изменяемые коммуналочки) и появился Докер. Для этого необходимо было переосмыслить определение и предназначение контейнера.

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

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

  • рантайм языка
  • пакеты-зависимости приложения
  • код самого приложения

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

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

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

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

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

Такой подход позволяет нам закрыть обе наши проблемы. С одной стороны мы расселили все коммуналочки, в крайних случаях доходя до контейнерного дзена и контейнера в один файл. С другой — мы убили Mutable monster‘а, утвердив единственный возможный способ внесения изменений в запущенные контейнеры только через пересборку образов с нуля, удаление старых контейнеров и создание на их месте новых.

Brave new world

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

Дело в том, что у большинства технологических прорывов есть две стороны медали:

  • решение старых проблем
  • открытие новых возможностей

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

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

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

Локализация разработки

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


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

  • этот сетап кто-то должен создать (и задокументировать)
  • от стабильности этого сетапа зависит процесс разработки (а значит — надо мониторить)
  • если этим сетапом будут пользоваться много разработчиков, он может начать тормозить (а значит — придется масштабировать)
  • усложняется процесс конфигурирования: необходимость переключаться между дев- и прод-окружениями
  • ну и как же не упомянуть аргумент “не покодить в самолете” :)

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

Докер позволяет одной командой развернуть весь стек сервисов, необходимых для разработки и тестирования приложения в виде отдельных контейнеров. И так же одной командой — бесследно их удалить. Типичный случай: пустые инстансы redis , rabbitmq , mongodb , в которых не хранится ничего важного.

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

Continuous Deployment

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

Унификация билд-серверов

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

Даже если вы разрабатываете один проект, или все ваши проекты работают на абсолютно одинаковом стеке, то никуда не уходит чистота сборочных и тестовых окружений. Мы должны либо озаботиться их очисткой после каждой сборки (да так, чтобы повторное создание этого окружения не длилось мучительно долго), либо довольствоваться тем, что каждый из наших пайплайнов — это маленький Mutable Monster, который в определенный момент может выстрелить нам в ногу.

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

  • сборка через docker build
  • тестирование через docker run
  • управление артефактами через docker push
  • развертывание через docker service create|update

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

Еще один слой уверенности

Благодаря неизменяемости докер-образов мы можем пойти на шаг дальше: вместо прогона сборки и тестов на каждый коммит, мы можем сразу же после успешных тестов выкатывать эти изменения на прод. Это и будет переход от Continuous Integration к Continuous Deployment.

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

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

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

Лечение и адаптация инфраструктуры

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

Подробнее про встроенный планировщик Swarm в этой статье.

Вместо послесловия

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

Для чего нужен Docker?

Делаем сейчас один небольшой проект, где будут 4 дублирующих друг друга сервера.
Внутри все просто — СУБД, сервер MQ, nginx, десяток сервисов — будет без Докера делать.

Но на больших масштабах, не рисковал бы.

Максим Жаров: Нет. Virtual Box — это полная виртаулизация. Машина с Виртуалбоком загрузится в лучшем случае пару минут (а как правило значительно дольше). Будет откусывать процентов 20% процессора и приличные накладные расходы на память.

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

Поэтому на вашем компьютере нельзя запустить 10 штук VirtualBox одновременно (или можно но будет дико тормозить), но при этом можно запустить несколько сотен Docker-контейнеров.

С другой стороны — ограничения на Docker — внутри может находиться только специально подготовленная операционная система Linux или FreeBSD.

Представьте что нет никакой ложки докера.

1) Есть одна физическая машина. Вы устанвливаете софт, разные приложухи, базы, web сервера, заходят тестовые юзеры, что-то запускают. Первая проблема — вы не понимаете кому что надо, кто владелец файлов, приложух, зачем висят демоны и кто за это ответственнен. Как выход, вы решаете это разделить на виртуалки.

2) У вас есть физическая машина + на ней виртуалки. Вы выделяете под каждую задачу свою виртуалку, там сидят отдельные пользователи, вы навели какой то порядок. Появляется задача — пользователи хотят php 6, а его нет, хотят python3, а его нет, хотят Mongo, а она старой версии. Вы обновляете репозитарии, качаете новые пакеты, ставите, часть пользователей довольны, часть нет — им нужна старая версия какая была. Упс!

3) Одна физическая машина + еще больше виртуальных машин. Вы разделили всех пользователей так, чтобы никто не дрался за версии софта, если нужен php6 — иди на эту машину, нужен php5 — вот на эту. Все счастливы, но появляются разработчики, которые говорят буквально так — «а у меня на рабочей машине все работает, я перенес все как было на виртуалку, а у меня появляется ошибка missing library libXXX.so.X». И вы понимаете что вам остается только создать полную копию машины разработчика, чтобы софт поехал на этой виртуалке без ошибок. И тут появляется Docker! :)

Цукерберг рекомендует:  Javascript - Веб приложения для пользователей от 13 до 17 лет

4) Docker решает именно эту проблему. Вам не нужно заботится о софте который установлен на сервере/виртуалке. Вы просто берете и переносите софт со всеми «кишками» на другой сервер и он просто работает. Работает за счет того, что все «кишки» это слои файловой системы нанизанные как бисер друг на друга. Дополнительно решается проблема свободного места, т.к слои многократно переиспользуются контейнерами, если вам нужен php + одна библиотека, а другому php + другая библиотека, вы используете (грубо говоря) слой php, а для дополнительной библиотеки делаете отдельный слой, одновременно другой человек делает над php другой слой и вы не деретесь между собой и не видите чужих библиотек. Это грубо и скорее всего ради одной библиотеки никто новый слой не делает, делают слой пожирнее.

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

viiy:
У нас то, что торчит наружу — nginx — не в контейнере.

А самописное — вестимо, в контейнере. Но вы его и не обновите из дистрибутивских реп. Оно же самописное.

viiy:
Можно обновлять (даже автоматически) контейнеры Докера (или создавать заново сразу со свежим ПО).

Если повезет — даже будет работать.

Но это нарушает концепцию CI с изолированным окружением — как только вы что-то меняете — все может сломаться. Но Докер здесь не при чем будет.

viiy:
Может у вас процесс неправильно поставлен?
У нас когда разработчик коммитит в Git запускаются тесты:

1. В индивидуально под тест подготавливаемом окружении (через Докер, естественно).
2. Если тест прошел — то эти же настроки идут в продакшн.
3. Но разработчик коммитит только свой небольшой кусок кода, и этот небольшой кусок кода и запускается в Докере отдельном.
4. Никто не разрешит разработчику свои настройки, как вы писали выше по библиотеки для PHP, затягивать в проект (у нас не PHP, но концепция, думаю, понятна).
5. Окружение, для, допустим, PHP (у нас его нет, в качестве примера), настраивает админ, максимум что может разработчик в конфигурационном файле прописать дополнительный требуемый модуль для PHP.
6. Таким образом, nginx/PHP и пр. вообще проходит мимо разработчика и он не может влиять. Не прошел тест — не сдал работу, денег не получил.

Если вам нужно запустить целый набор демоном, тут появляются проблемы, нужно писать шелл-скрипт, который все это поднимет в контейнере

Идеология Docker предполагает 1 сервис = 1 контейнер.
Запуск множества контейнеров давно решен даже с перебором — т.н. «средства оркестрации». Вплоть до того, что они сами поместят Docker-контейнер на свободную машину, сами запустят.

Ковырять с сторону:

Nomad, Terraform, Mesos, Maraphon, Aurora, Kubernetes, Docker Swarm, HTCondor, AWS ECS, Hadoop YARN, Yandex Cocaine

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

Или админов, которые леняться установить нужные версии.

Для решения этой проблемы и придуман Docker.

Одна физическая машина и куча виртуалок? Думаю, что сильно тормозить будет

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

А виртуализация с Docker используется только на машинах разработчиков, которые не хотят работать под Linux, разрабатывая сервера под Linux, а работают под Windows, MacOSX.

В production если используется Docker, то в качетве базовой ОС используется Linux (изредка FreeBSD, там Docker пока экспериментален).

Так что ничего не тормозит, ибо Docker — это не виртуализация под Linux/FreeBSD, а вполне нативное исполнение

Внутри Docker только Linux, и, экспериментально, FreeBSD. Запускается нативно под Linux и, экспериментально, под FreeBSD. Под MacOSX, Windows — через виртуальную машину.

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

Это почти виртуальная машина. Почти, да не совсем.

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

Например, мы используем Yandex.Cocaine, которая нормально компилируется только на Ubuntu 14.04 (и, вроде, на Debian 7). Но не под CentOS 6, 7, Debian 8, FreeBSD 9, 10, Ubuntu 15, 16 и пр. — скомпилировать его невозможно. Запускаем в этих операционных системах в Докере.

С другой стороны, и одновременно с этим, вам необходимо установить другое, более современное ПО. И одновременно более старое. Причем речь даже не идет об серьезно отличающихся версиях Linux. Например, одно ПО требует не менее Ubuntu 14.10, а другое не более Linux 14.04.

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


Таким образом, мы имеем бинарный файл запускаемый как бы в своей операционной системе.

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

Но виртуальные машины с Докером используются только для разработки. Для запуска в production виртуальные машины с Докер не используются.

Докер использует контейнеры операционной системы. LXC в Linux, Jails в FreeBSD. Контейнер — это область операционной системы, изолированная от основной части операционной системы. В контейнере свое дерево каталогов (включая системные /dev, /bin, /sbin и пр.), свои сетевые порты и пр. и пр.

Но при этом не используется полная виртуализация. Что существенно экономит ресурсы. Запустить 100 полноценных виртуальных машин вряд ли получится даже на мощном сервере. А вот запустить 100 контейнеров Docker даже на слабом домашнем компьютере — возможно.

Правда использование не полной виртуализации ограничивает использование операционных систем внутри контейнеров. Как правило, это специально подготовленные версии Linux или FreeBSD. Именно специально подготовленные. Windows — в принципе в контейнере запустить невозможно.

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

Зачем это используется?

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

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

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

Более того — изначально разработчик программного обеспечения тестирует программу в контейнере Докер, с определенными настроками. И в этом же (или с такими же настройками) контейнере Докера программа уезжает на сервер.

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

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

Потом плюнули на попытки упорядочить окружение в ОС — и сейчас концепция такова — устанавливать программы на сервера вместе со своими индивидуально настроенными под них операционными системами — то есть внутри контейнеров. 1 контейнер = 1 настройка ОС = 1 программа внутри.

Другими словами:

  • Докер-контейнер нужно использовать для отладки.
  • Тот же Докер-контейнер нужно использовать и на сервере.

Это позволяет не трудиться с настройками «под сервер» локально на машине разработчика. Это позволяет разрабатывать на машине разработчика совершенно разные программы одновременно, которые требует несовместимых настроек операционной системы. Это позволяет давать гораздо больше гарантий, что программа на сервере будет вести себя также как и на машине разработчика. Это позволяет разрабатывать под Windows/MacOSX с удобным «прозрачным» тестированием под Linux.

Докер применим к созданию/настройке только серверного программного обеспечения под Linux (экспериментально под FreeBSD). Не для смартфонов. А если десктопов — то только программное обеспечение без GUI.

Посколько Докер позволил одним махом упростить работу разработчикам и админам и повысить качество результата — сейчас бум на Докер. Придумано огромная гора инструментов для управления развертыванием приложений созданных с Докером. Если раньше чтобы запустить 10 000 программ на 1000 серверах нужно было как минимум 3 высококвалифицированнейших девопса, которые писали кучу описаний как это сделать на Puppet, Salt, Chef, Ansible, да и то не было гарантий, это все тестилось месяцами. То сейчас с Докер даже один квалифицированных девопс может рулить миллионами программ на десятках тысяч серверов. С куда как большей гарантией, что все это заведется нормально.

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

Разработчик отдает весь свой результат в систему CI (обычно через git)
CI на каждый новый коммит делает с помощью Docker образ для тестирования.
Если тесты проходят успешно, то этот же самый Docker образ, отправляется на развертывание в production.
Или, чуть иначе в компилируемых системах, где исходники не нужны в production: в Docker производится развертывание среды для компиляции, а для тестирования разворачивается второй образ с уже откомпилированным добром, который уже отправляется в production.

То есть при правильной огранизации дела разработчик не может/не должен влиять на то, какой будет образ.
А вот в тестовой среде (запускаемом на сервер, недоступном разработчику в больших командах) и в production как раз используется один и тот же образ.

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

Общие сведения о контейнерах и Docker Introduction to Containers and Docker

Контейнеризация — это подход к разработке программного обеспечения, при котором приложение или служба, их зависимости и конфигурация (абстрактные файлы манифеста развертывания) упаковываются вместе в образ контейнера. Containerization is an approach to software development in which an application or service, its dependencies, and its configuration (abstracted as deployment manifest files) are packaged together as a container image. Контейнерное приложение может тестироваться как единое целое и развертываться как экземпляр образа контейнера в операционной системе (ОС) узла. The containerized application can be tested as a unit and deployed as a container image instance to the host operating system (OS).

Так же как обычные контейнеры позволяют перевозить любые грузы на корабле, поезде или грузовике, программные контейнеры выступают в качестве стандартных модулей для развертывания программного обеспечения, которые могут содержать различный код и зависимости. Just as shipping containers allow goods to be transported by ship, train, or truck regardless of the cargo inside, software containers act as a standard unit of software deployment that can contain different code and dependencies. Контейнеризация программного обеспечения позволяет разработчикам и ИТ-специалистам развертывать его в разных средах без каких-либо изменений или с минимальными изменениями. Containerizing software this way enables developers and IT professionals to deploy them across environments with little or no modification.

Контейнеры также изолируют приложения друг от друга в общей операционной системе. Containers also isolate applications from each other on a shared OS. Контейнерные приложения выполняются на основе узла контейнеров, который в свою очередь работает в операционной системе (Linux или Windows). Containerized applications run on top of a container host that in turn runs on the OS (Linux or Windows). Поэтому контейнеры требуют гораздо меньше ресурсов, чем образы виртуальных машин. Containers therefore have a significantly smaller footprint than virtual machine (VM) images.

Каждый контейнер может вмещать целое веб-приложение или службу, как показано на рис. 2-1. Each container can run a whole web application or a service, as shown in Figure 2-1. В этом примере узел Docker — это узел контейнеров, а App1, App2, Svc 1 и Svc 2 — контейнерные приложения или службы. In this example, Docker host is a container host, and App1, App2, Svc 1, and Svc 2 are containerized applications or services.

Рис. 2-1. Figure 2-1. Несколько контейнеров на одном узле Multiple containers running on a container host

Еще одним преимуществом контейнеризации является масштабируемость. Another benefit of containerization is scalability. Вы можете быстро осуществлять горизонтальное масштабирование, создавая контейнеры для краткосрочных задач. You can scale out quickly by creating new containers for short-term tasks. С точки зрения приложения, создание экземпляра образа (контейнера) аналогично созданию экземпляра процесса, например для службы или веб-приложения. From an application point of view, instantiating an image (creating a container) is similar to instantiating a process like a service or web app. Но для обеспечения надежности при запуске нескольких экземпляров одного образа на нескольких серверах обычно желательно, чтобы контейнеры (экземпляры образа) выполнялись на разных серверах или виртуальных машинах в разных доменах сбоя. For reliability, however, when you run multiple instances of the same image across multiple host servers, you typically want each container (image instance) to run in a different host server or VM in different fault domains.

Иными словами, контейнеры предоставляют такие преимущества, как изоляция, переносимость, гибкость, масштабируемость и контроль, на протяжении всего жизненного цикла приложения. In short, containers offer the benefits of isolation, portability, agility, scalability, and control across the whole application lifecycle workflow. Самым важным преимуществом является изоляция среды разработки от рабочей среды. The most important benefit is the environment’s isolation provided between Dev and Ops.

Docker: контейнеры — это просто

Что такое Docker

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

Docker был написан на языке программирования Go и выпущен в 2013 году. Изначально он работал только с Linux-системами, однако на данный момент его можно использовать также в Windows и macOS. Несмотря на то, что проект является относительно новым, он уже широко используется многими специалистами и продолжает завоевывать популярность.

Важной частью экосистемы Docker является Docker Hub — открытый репозиторий образов контейнеров. В нём можно найти десятки готовых приложений от официальных разработчиков. Среди них — nginx, MySQL, Apache, Gitlab, Redmine, Elasticsearch, Jenkins и другие.

Например, для запуска WordPress с помощью Docker достаточно выполнить следующие команды:

После этого откройте в браузере страницу http://12.34.56.78 (здесь укажите реальный IP-адрес вашего VDS) и приступите к настройке CMS!

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

  • образ (image) – шаблон, который используется для создания контейнеров. Представляет собой слепок файловой системы, в котором расположен код приложения и его окружение;
  • реестр (registry) – репозиторий образов. Docker Hub, о котором шла речь выше, — это публичный репозиторий, где хранится огромное количество образов;
  • контейнер (container) – запущенное приложение, т.е. совокупность процессов и образа.

Преимущества Docker

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

Возьмем ситуацию, когда вам нужно установить Redmine, Github, Jenkins или что-либо другое. В обычной ситуации вы бы самостоятельно устанавливали пакеты, настраивали окружение, а также отдельно устанавливали бы веб-сервер. С Docker все намного проще – вам просто необходимо запустить готовый контейнер из репозитория hub.docker.com. В базе Docker содержится более 100 тысяч готовых к установке приложений, среди которых вы точно найдете нужное.

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

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

Docker в VDS Timeweb

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

Для того, чтобы начать использовать Docker, сначала вам необходимо установить его – это можно сделать двумя путями.

  1. Вы можете установить Docker в панели управления VDS при создании нового сервера: на 2-м шаге «Программное обеспечение» при выборе Ubuntu 16.04 в качестве операционной системы вы сможете установить DockerUI.
  2. Также вы можете провести установку через консоль любого дистрибутива Linux, следуя инструкции на официальном сайте: https://docs.docker.com/engine/installation/linux/

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

Как и во многих других случаях, работа с Docker напрямую через консоль отличается большей стабильностью и более широкими возможностями. Все необходимые команды вы найдете на этой странице официального сайта: https://docs.docker.com/engine/reference/commandline/

Однако начинающим пользователям, которые хотят познакомиться с Docker, будет удобнее работать в DockerUI:

ИТ База знаний

ShareIT — поделись знаниями!

Полезно

Узнать IP — адрес компьютера в интернете

Онлайн генератор устойчивых паролей

Онлайн калькулятор подсетей

Калькулятор инсталляции IP — АТС Asterisk

Руководство администратора FreePBX на русском языке


Руководство администратора Cisco UCM/CME на русском языке

Серверные решения

Телефония

FreePBX и Asterisk

Настройка программных телефонов

Корпоративные сети

Похожее и популярное

Пошаговый ввод в домен Windows 10

Apache или IIS – сравнение и преимущества

Погружение в Iptables – теория и настройка

Как восстановить пароль от root в CentOS 7

Разбираемся с Docker: установка и использование

Приложение в контейнере

Сегодня речь в статье пойдет о Docker. Все, кто хоть как-то касаются сферы IT слышали про Docker, но не все знают, что же это такое. Итак, сегодня мы простыми словами расскажем о том, что такое Docker, чем это отличается от виртуализации, покажем подробный процесс инсталляции на CentOS 7 и установим просто графический интерфейс Portainer, для управления контейнерами. Также немного коснемся команд для использования Docker.

Что такое Docker?

Docker — это платформа, которая может “упаковать” приложение, его зависимости, middleware и так далее в так называемый “контейнер”, после чего у вас появится возможность развернуть данный контейнер на любом сервере, на котором установлен Docker — причем буквально за доли секунды, одной командой.

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

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

Установка Docker

Как было упомянуто в начале статьи, устанавливать Докер мы будем на CentOS 7 — процесс установки крайне простой и быстрый.

Итак, сначала необходимо установить с помощью yum несколько пакетов:

Далее необходимо установить stable репозиторий для Докера, который вам понадобится, даже если вы захотите устанавливать билды из edge и test репозиториев:

Затем устанавливаем сам Docker:

yum install docker-ce

И, наконец, запускаем Docker:

Проверяем, что Docker запустился и работает в два шага:

Вы должны увидеть следующий вывод:

После этого пробуем развернуть контейнер hello-world:

Если все шаги были выполнены корректно, то на экране должно появится следующее:

Установка Portainer

Portainer — это очень удобный графический интерфейс для управления Docker или Docker Swarm. Устанавливается он практически в одно действие — так как сам точно также является контейнером. Итак:

Создаем разметку для Portainer:

И затем запускаем сам контейнер:

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

Далее выбираем где находится наш Докер — на этом же сервере, или на другом (в нашем случае — Local) и кликаем Connect.

После чего вас встретит красивый дэшборд:

Я предлагаю вам попробовать разобраться со всем многообразием дэшборда самим и задавать нам вопросы в комментариях — а мы пока продемонстрируем несколько фич.

Итак, сначала кликните на Containers — вы увидите все имеющиеся контейнеры с информацией о них:

Как вы можете видеть, у нас на данный момент запущен только один контейнер — Portainer, и доступ к нему открыт по порту 9000 (столбец Published Ports), и адрес во внутренней сети Docker — 172.17.0.2.

Далее кликните на App Templates в меню справа — и вы увидите весь список приложений, который можно запустить одним кликом:

Зайдем во вкладку Httpd:

Сперва, назовите данный контейнер как-нибудь — мы назвали test-merionet. Затем, можете кликнуть на Show advanced options и вы увидите возможность выбора какой порт, протокол и том будет использоваться данным контейнером. Затем просто нажмите на Deploy the container.

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

Отсюда вы увидите, что httpd сервер доступен на 32768 порту. Итак, пробуем зайти на данный сервер через браузер:

Вы должны будете увидеть надпись It works! так же как на скриншоте выше — дальнейшую настройку httpd мы пока оставляем за кадром.

Донастройка Docker и полезные команды

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

Первым делом, настройте автозапуск для сервиса Docker:

Затем, вы можете проверить запущенные контейнеры в консоли (на случай если вам не нравится идея использования GUI) с помощью команды

Теперь немного о командах и синтаксисе — будем показывать на примерах:

Допустим, нам нужно запустить CentOS и выполнить в нем команду echo:

Запустить CentOS и подключиться к его терминалу:

Можете сразу указать нужные порты с помощью ключа -p и запустить контейнер в бэкграунде с помощью ключа —d:

Итак, совсем немного об опциях для команды docker run — полный список можно найти по ссылке https://docs.docker.com/engine/reference/commandline/run/#description

  • -p — открываем конкретные порты через пробел — порт доступа и порт на контейнере, к примеру docker run -p 9876:80 %imagename%
  • -P — открываем сразу все порты;
  • -t — подключение к терминалу контейнера;
  • -i — интерактивный режим, STDIN все время будет открыт;

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

Заключение

На этом всё, спасибо за внимание! Пишите в комментариях, что еще вам интересно узнать про Докер — в следующих статьях мы покроем такие темы как: создание своего собственного Докер-файла и образа, как подключить папку файловой системы с вашего хоста, интересное на Docker Hub и так далее.

Полезна ли Вам эта статья?

Пожалуйста, расскажите почему?

Нам жаль, что статья не была полезна для вас :( Пожалуйста, если не затруднит, укажите по какой причине? Мы будем очень благодарны за подробный ответ. Спасибо, что помогаете нам стать лучше!

Подпишитесь на нашу еженедельную рассылку, и мы будем присылать самые интересные публикации :) Просто оставьте свои данные в форме ниже.

Понравилась статья? Поделиться с друзьями:
Все языки программирования для начинающих