Что представляет собой контейнер? Контейнеры — это способ упаковать код, чтобы позволить ему работать на любой машине. Для работы контейнера ОС выделяет пул изолированных ресурсов: память, ядра ЦП, диск и сетевое пространство. Контейнер работает на ядре хостовой ОС и отделяется средствами ОС, а не возможностями железа, как ВМ (виртуальная машина).
В Linux есть только несколько инструментов ядра, которыми можно ограничить доступ к ресурсам и изолировать системные процессы. С помощью Namespaces (неймспейсы) процессы объединяют в группы и изолируют. При этом с помощью cgroups можно задать лимиты по ресурсам. Cgroups — аббревиатура от Linux «control groups». Определяется как функция ядра Linux, контролирующая распределение ресурсов для пользовательских процессов.
Код может быть написан на разных языках, опираться на другое программное обеспечение, устанавливать пакеты из интернет-репозитория. Для его правильной работы необходимы определенные системные требования (например, объем памяти). При контейнеризации контейнеры содержат все компоненты, необходимые для запуска программы. Контейнер командует своей собственной средой выполнения, которая включает в себя то, что позволяет программе быть исполняемой, например, файлы, окружения, библиотеки.
Механизмы контейнеризации отличаются от традиционных подходов виртуализации. Контейнеры универсальны - они способны функционировать на обычных и облачных серверах и на отдельной виртуальной машине.
Разница между контейнерами и виртуальными машинами заключается в следующем. Контейнер - легкая среда, которую создают для размещения одного или нескольких изолированных приложений на пустой платформе. Необходимо выбрать виртуальные машины, когда следует разместить целую операционную систему или экосистему, а также запустить приложения, несовместимые с базовой средой.
Контейнеры устанавливают все необходимые файлы для запуска используемого программного обеспечения.
С помощью контейнеризации разработчики могут сами создавать ПО, чтобы гарантировать, что оно будет эффективно работать и не даст сбоев.
При контейнеризации есть два файла спецификации, которые определяют, какое ПО требуется на машине и какое оборудование необходимо для ее работы. Это:
После создания контейнера формируется образ, который задается на выбранном узле.
Если речь идет о виртуализации серверов, то обычно имеют в виду виртуальную машину. ВМ создает гипервизорный слой между операционной системой, приложениями и сервисами, а также памятью, хранилищем и т.д.
Этот слой действует как собственная виртуальная машина, создавая автономную среду для запуска одного приложения. Каждое приложение, которое виртуализируется, потребляет свою собственную версию ОС. Для разработки в виртуальной среде необходимо иметь несколько доступных версий ОС либо приобретать лицензии.
С другой стороны, контейнеры позволяют запускать несколько приложений на одной ВМ. Это ограничивает количество лицензий на ПО, в которые корпоративная компания должна инвестировать. Достигается такое за счет совместного использования ядер ОС, вместо того чтобы требовать свои собственные. По этой причине разработка в контейнере - это более ресурсоемкий подход к разработке корпоративного программного обеспечения, как в финансовом, так и в вычислительном плане.
Контейнеры - это решение многих проблем, возникающих при разработке кода:
Преимущества развертывания ПО на контейнерном движке приведены ниже.
Одним из наиболее приоритетных преимуществ использования контейнеров в качестве метода виртуализации является возможность работы в облаке. Многие движки поддерживают мультиоблачные платформы, поэтому их можно запускать внутри таких платформ, как:
Контейнеры упаковывают код для отправки любым путем. При этом неважно, какой агент занимается доставкой, что дает клиенту больше возможностей выбирать из разных поставщиков.
Контейнеризация дает предприятиям гибкость в создании, тестировании и выпуске образов для развертывания на нескольких серверах.
Хотя согласованность между средами иногда нарушается, особенно когда речь идет о циклах разработки и выпуска, поставщики контейнеров, такие как Docker, упрощают обеспечение согласованности независимо от того, в какой среде вы развертываете свой образ. Следовательно, контейнеризация - хороший вариант для организаций, использующих DevOps для ускорения доставки приложений.
Чтобы оставаться конкурентоспособными, контейнерные платформы должны обеспечивать контроль версий.
Docker предлагает упрощенный контроль версий, позволяющий легко вернуться к предыдущему образу в случае сбоя среды.
Ключевым компонентом виртуализации является изоляция - выделение ресурсов для каждого приложения. Контейнерные движки работают лучше, чем виртуальные машины, когда речь идет об изоляции. Однако выбор в пользу одного из них требует обдумывания и зависит от конкретного случая использования.
Контейнеры, по сравнению с виртуальными машинами, отличаются:
Когда Docker только появился, безопасность была слабой. Позже Docker исправил многие из основных проблем, например, запуск каждого контейнера из корневой папки.
Поставщики контейнеров предлагают различные способы обеспечения безопасности. Однако при этом соблюдается важное условие: если один из ваших контейнеров взломают, приложения, работающие в других контейнерах, не пострадают.
Тем не менее, отдельные контейнеры подвержены атакам, но существуют способы их защиты с помощью особых служб мониторинга и современных методик.
Контейнеры используют многие разработчики, так как они являются универсальным и ресурсосберегающим подходом к разработке ПО. Компании могут обеспечить удовлетворение всех своих потребностей в разработке и развертывании, экономя при этом серверное пространство, как виртуальное, так и облачное.
По сравнению с виртуальными машинами, контейнеры занимают меньше места. С их помощью можно ограничить потребление минимального количества ресурсов.
Поскольку контейнерные приложения могут работать на облачных серверах, они обычно более доступны, чем другие приложения. Программирование в контейнерах обеспечивает переносимость подхода к разработке ПО.
DevOps любит контейнеризацию - она имеет такое важное свойство, как воспроизводимость. Компоненты каждого контейнера остаются статичными и неизменными от кода до развертывания. Они создают единый образ, который можно воспроизводить в других контейнерах снова и снова.
Для любой среды разработки, требующей портативности и универсальности, DevOps и контейнеры предлагают оптимальное решение. Этому способствуют две ключевые тенденции:
В то время как все больше компаний преодолевают непростые трудности, связанные с обеспечением того, чтобы их облачные ресурсы были настроены, интегрированы, автоматизированы и функционировали должным образом, потребность в квалифицированных специалистах DevOps резко возросла.
Многие компании нацелены на мобильность. Они работают из удаленных мест с доступом в Интернет, часто преодолевая часовые пояса и установленные рабочие часы.
Контейнеризация предлагает именно то, что они ищут - пакет для программного обеспечения, который поставляется вместе. Контейнеры обеспечивают стабильную производительность в разных часовых поясах и на разных устройствах.
Контейнеры прекрасно подходят для любого предприятия или организации, которые стремятся повысить эффективность управления цифровым предприятием с помощью решений, обеспечивающих надежность, мобильность, универсальность и воспроизводимость в виртуальной среде. В механизмах контейнеризации поможет разобраться компания timeweb.cloud.
При внедрении контейнеров рекомендуется выполнить следующие шаги:
Cgroups являются ключевым свойством, с помощью которого можно контролировать выделение ресурсов для конкретного процесса либо группы процессов (tasks). Cpuset Linux показывает, как отдельный контейнер может использовать памяти (Ram группа), cgroups CPU или средства ввода/ вывода. Каждый системный ресурс - это подсистема. Для подсистем создается иерархия. Каждый корневой каталог иерархии (корневая cgroup) и его подкаталоги (дочерние cgroup) содержат собственные файлы конфигурации, устанавливающие ограничения на распределение ресурсов операционной системы.
Kubernetes cgroup работает по умолчанию с двумя типами ресурсов: CPU и RAM. Каждый вычислительный узел в кластере, контролируемый Kubernetes, содержит определенные ресурсы.
Отдельные программные приложения нуждаются в контроле или ограничении. Это необходимо как с целью стабильности, так и безопасности. Часто одна ошибка или просто плохой код может нарушить работу всей машины и привести к потенциальному сбою в экосистеме. Но есть способ держать приложения под контролем. Группы управления (cgroups) - функция ядра, с помощью которой можно ограничивать, оценивать и изолировать использование процессора, памяти, дискового ввода-вывода и сети одним или несколькими процессами.
Первоначально разработанная Google, технология cgroups в конечном итоге попала в основную часть ядра Linux в версии 2.6.24 (январь 2008 года). Переработка этой технологии - то есть добавление kernfs (для разделения части логики sysfs) - будет включена в ядра 3.15 и 3.16.
Основной целью разработки cgroups было предоставление унифицированного интерфейса для управления процессами или виртуализацией на уровне всей операционной системы, включая Linux Containers или LXC. Фреймворк cgroups обеспечивает следующее:
Группа cgroup может состоять из одного или нескольких процессов, которые привязаны к одному и тому же набору ограничений. Эти группы могут быть иерархическими - подгруппа наследует ограничения, установленные для ее родительской группы.
Ядро Linux предоставляет доступ к ряду контроллеров или подсистем для технологии cgroup. Контроллер отвечает за распределение определенного типа системных ресурсов для набора одного или нескольких процессов. Например, контроллер памяти ограничивает использование памяти, а контроллер cpuacct следит за использованием процессора.
Вы можете получить доступ к cgroups и управлять ими как напрямую, так и косвенно (с помощью LXC, libvirt или Docker). Рассмотрим на примере. В Red Hat Enterprise Linux или CentOS введите в командной строке следующее:
$ sudo yum install libcgroup libcgroup-tools
Ручной способ
Установив соответствующие пакеты, вы можете конфигурировать cgroups непосредственно через иерархию sysfs. Например, чтобы создать cgroup с именем foo в подсистеме памяти, создайте каталог с именем foo в /sys/fs/cgroup/memory:
$ sudo mkdir /sys/fs/cgroup/memory/foo
По умолчанию каждая вновь созданная cgroup наследует доступ ко всему пулу памяти системы. Для некоторых приложений, в первую очередь тех, которые продолжают выделять все больше памяти, но отказываются освобождать уже выделенную, это не совсем правильно. Чтобы ограничить приложение до разумного предела, необходимо обновить файл memory.limit_in_bytes.
Ограничьте память для всего, что работает под cgroup foo, до 50 МБ:
$ echo 50000000 | sudo tee /sys/fs/cgroup/memory/foo/memory.limit_in_bytes
Проверьте настройки:
$ sudo cat memory.limit_in_bytes
50003968
Обратите внимание, что значение, считанное обратно, всегда будет кратно размеру страницы ядра (то есть 4096 байт или 4 КБ). Это значение является наименьшим размером памяти, который можно выделить.
Запустите приложение:
$ sh ~/test.sh &
Используя идентификатор процесса (PID), переместите приложение в cgroup foo под контроллером памяти:
$ echo 2845 /sys/fs/cgroup/memory/foo/cgroup.procs
Используя тот же номер PID, перечислите запущенный процесс и убедитесь, что он запущен в нужной cgroup:
$ ps -o cgroup 2845
CGROUP
8:memory:/foo,1:name=systemd:/user.slice/user-0.slice/
session-4.scope
Вы также можете следить за тем, что в настоящее время используется этой cgroup, читая нужные файлы. В данном случае вы можете увидеть объем памяти, выделенной вашему процессу (подпроцессам):
$ cat /sys/fs/cgroup/memory/foo/memory.usage_in_bytes
253952
Теперь давайте повторим тот же сценарий. Вместо того, чтобы ограничить cgroup foo 50 МБ памяти, ограничьте ее 500 байтами:
$ echo 500 | sudo tee /sys/fs/cgroup/memory/foo/
memory.limit_in_bytes
Примечание: если задача выходит за установленные пределы, ядро вмешается и способно уничтожить эту задачу.
Если считать значение обратно, то оно будет кратно размеру страницы ядра. Несмотря на то, что было установлено значение 500 байт, на самом деле оно равно 4 КБ:
$ cat /sys/fs/cgroup/memory/foo/memory.limit_in_bytes
4096
Запустите приложение, переместите его в группу cgroup, следите за системными журналами.
Итак, Out-Of-Memory Killer (или oom-killer) ядра вмешался, как только приложение достигло предела в 4 КБ. Оно уничтожило приложение, которое больше не выполняется. Вы можете убедиться в этом, набрав:
$ ps -o cgroup 2687
CGROUP
Многие из описанных выше шагов упрощаются благодаря утилитам администрирования, входящим в пакет libcgroup. Например, процесс создания записей и файлов sysfs выполняется одним вызовом команды cgcreate.
Чтобы создать группу с именем foo в подсистеме памяти, введите следующее:
$ sudo cgcreate -g memory:foo
Примечание: libcgroup предоставляет механизм для управления задачами в группах управления.
Используя те же методы, что и раньше, можно начать устанавливать пороговые значения:
$ echo 50000000 | sudo tee
/sys/fs/cgroup/memory/foo/memory.limit_in_bytes
Проверка настроенных параметров:
$ sudo cat memory.limit_in_bytes
50003968
Запустите приложение в cgroup foo с помощью бинарного файла cgexec:
$ sudo cgexec -g memory:foo ~/test.sh
Используя его PID номер, проверьте, что приложение запущено в cgroup и под определенной подсистемой (память):
$ ps -o cgroup 2945
CGROUP
6:memory:/foo,1:name=systemd:/user.slice/user-0.slice/session-1.scope
Если ваше приложение больше не работает, и вы хотите очистить и удалить cgroup, можно сделать это с помощью двоичного файла cgdelete. Чтобы удалить группу foo из-под контроллера памяти, введите:
$ sudo cgdelete memory:foo
Вы также можете выполнить все вышеперечисленное с помощью простого конфигурационного файла и запуска службы. Можно определить все имена и атрибуты cgroup в файле /etc/cgconfig.conf.
Параметры cpu.shares определяют приоритет ЦП для группы. По умолчанию все группы наследуют 1,024 доли или 100% процессорного времени. Если уменьшить это значение до более консервативного, например, 100, то группа будет ограничена примерно 10% процессорного времени.
Процесс, запущенный в cgroup, также может быть ограничен количеством CPU (ядер), к которым он может получить доступ. Добавьте следующий раздел в тот же файл cgconfig.conf и под нужным именем группы:
cpuset {
cpuset.cpus="0-5";
}
При таком ограничении эта cgroup привяжет приложение к ядрам от 0 до 5 - то есть оно будет видеть только первые шесть ядер процессора в системе.
Далее необходимо загрузить эту конфигурацию с помощью службы cgconfig. Сначала включите cgconfig для загрузки вышеуказанной конфигурации при загрузке системы:
$ sudo systemctl enable cgconfig
Create symlink from /etc/systemd/system/sysinit.target.wants/
cgconfig.service
to /usr/lib/systemd/system/cgconfig.service.
Теперь запустите службу cgconfig и загрузите тот же файл конфигурации вручную (можно пропустить этот шаг и перезагрузить систему):
$ sudo systemctl start cgconfig
Запустите приложение в cgroup foo и привяжите его к ограничениям памяти и процессора:
$ sudo cgexec -g memory,cpu,cpuset:foo ~/test.sh &
За исключением запуска приложения в предопределенной cgroup, все остальное будет сохраняться при перезагрузке системы. Но можно автоматизировать этот процесс, определив сценарий запуска init, зависящий от службы cgconfig, для запуска того же приложения.
Часто возникает необходимость ограничить одну или несколько задач на машине. Группы управления обеспечивают такую функциональность, и, используя ее, можно наложить строгие аппаратные и программные ограничения на некоторые из самых важных или неконтролируемых приложений. Если одно приложение не устанавливает верхний порог или не ограничивает объем памяти, который оно может потреблять в системе, то cgroups может решить эту проблему. Если другое приложение имеет тенденцию быть требовательным к процессору, то cgroups поможет в этом. С помощью cgroups можно добиться многого, и при малых потерях времени, вернуть стабильность, безопасность и надежность в свою операционную среду. Это же касается и cgroups Windows.